VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/utils/fs/FsPerf.cpp@ 76917

Last change on this file since 76917 was 76917, checked in by vboxsync, 6 years ago

FsPerf: Updates. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 101.6 KB
Line 
1/* $Id: FsPerf.cpp 76917 2019-01-21 12:50:59Z vboxsync $ */
2/** @file
3 * FsPerf - File System (Shared Folders) Performance Benchmark.
4 */
5
6/*
7 * Copyright (C) 2019 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/alloca.h>
32#include <iprt/asm.h>
33#include <iprt/assert.h>
34#include <iprt/err.h>
35#include <iprt/dir.h>
36#include <iprt/file.h>
37#include <iprt/getopt.h>
38#include <iprt/initterm.h>
39#include <iprt/list.h>
40#include <iprt/mem.h>
41#include <iprt/message.h>
42#include <iprt/param.h>
43#include <iprt/path.h>
44#include <iprt/process.h>
45#include <iprt/rand.h>
46#include <iprt/string.h>
47#include <iprt/stream.h>
48#include <iprt/test.h>
49#include <iprt/time.h>
50#include <iprt/thread.h>
51#include <iprt/zero.h>
52
53#ifdef RT_OS_WINDOWS
54# include <iprt/nt/nt-and-windows.h>
55#endif
56
57
58/*********************************************************************************************************************************
59* Defined Constants And Macros *
60*********************************************************************************************************************************/
61/**
62 * Macro for profiling @a a_fnCall (typically forced inline) for about @a a_cNsTarget ns.
63 *
64 * Always does an even number of iterations.
65 */
66#define PROFILE_FN(a_fnCall, a_cNsTarget, a_szDesc) \
67 do { \
68 /* Estimate how many iterations we need to fill up the given timeslot: */ \
69 fsPerfYield(); \
70 uint64_t nsStart = RTTimeNanoTS(); \
71 uint64_t ns; \
72 do \
73 ns = RTTimeNanoTS(); \
74 while (ns == nsStart); \
75 nsStart = ns; \
76 \
77 uint64_t iIteration = 0; \
78 do \
79 { \
80 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
81 iIteration++; \
82 ns = RTTimeNanoTS() - nsStart; \
83 } while (ns < RT_NS_10MS || (iIteration & 1)); \
84 ns /= iIteration; \
85 if (ns > g_nsPerNanoTSCall + 32) \
86 ns -= g_nsPerNanoTSCall; \
87 \
88 uint64_t cIterations = (a_cNsTarget) / ns; \
89 if (cIterations <= 1) \
90 cIterations = 2; \
91 else if (cIterations & 1) \
92 cIterations++; \
93 \
94 /* Do the actual profiling: */ \
95 fsPerfYield(); \
96 iIteration = 0; \
97 nsStart = RTTimeNanoTS(); \
98 for (; iIteration < cIterations; iIteration++) \
99 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
100 ns = RTTimeNanoTS() - nsStart; \
101 RTTestIValueF(ns / cIterations, RTTESTUNIT_NS_PER_OCCURRENCE, a_szDesc); \
102 if (g_fShowDuration) \
103 RTTestIValueF(ns, RTTESTUNIT_NS, "%s duration", a_szDesc); \
104 } while (0)
105
106
107/**
108 * Macro for profiling an operation on each file in the manytree directory tree.
109 *
110 * Always does an even number of tree iterations.
111 */
112#define PROFILE_MANYTREE_FN(a_szPath, a_fnCall, a_cEstimationIterations, a_cNsTarget, a_szDesc) \
113 do { \
114 if (!g_fManyFiles) \
115 break; \
116 \
117 /* Estimate how many iterations we need to fill up the given timeslot: */ \
118 fsPerfYield(); \
119 uint64_t nsStart = RTTimeNanoTS(); \
120 uint64_t ns; \
121 do \
122 ns = RTTimeNanoTS(); \
123 while (ns == nsStart); \
124 nsStart = ns; \
125 \
126 PFSPERFNAMEENTRY pCur; \
127 uint64_t iIteration = 0; \
128 do \
129 { \
130 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
131 { \
132 memcpy(a_szPath, pCur->szName, pCur->cchName); \
133 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
134 { \
135 RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
136 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
137 } \
138 } \
139 iIteration++; \
140 ns = RTTimeNanoTS() - nsStart; \
141 } while (ns < RT_NS_10MS || (iIteration & 1)); \
142 ns /= iIteration; \
143 if (ns > g_nsPerNanoTSCall + 32) \
144 ns -= g_nsPerNanoTSCall; \
145 \
146 uint32_t cIterations = (a_cNsTarget) / ns; \
147 if (cIterations <= 1) \
148 cIterations = 2; \
149 else if (cIterations & 1) \
150 cIterations++; \
151 \
152 /* Do the actual profiling: */ \
153 fsPerfYield(); \
154 uint32_t cCalls = 0; \
155 nsStart = RTTimeNanoTS(); \
156 for (iIteration = 0; iIteration < cIterations; iIteration++) \
157 { \
158 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
159 { \
160 memcpy(a_szPath, pCur->szName, pCur->cchName); \
161 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
162 { \
163 RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
164 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
165 cCalls++; \
166 } \
167 } \
168 } \
169 ns = RTTimeNanoTS() - nsStart; \
170 RTTestIValueF(ns / cCalls, RTTESTUNIT_NS_PER_OCCURRENCE, a_szDesc); \
171 if (g_fShowDuration) \
172 RTTestIValueF(ns, RTTESTUNIT_NS, "%s duration", a_szDesc); \
173 } while (0)
174
175
176/**
177 * Execute a_fnCall for each file in the manytree.
178 */
179#define DO_MANYTREE_FN(a_szPath, a_fnCall) \
180 do { \
181 PFSPERFNAMEENTRY pCur; \
182 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
183 { \
184 memcpy(a_szPath, pCur->szName, pCur->cchName); \
185 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
186 { \
187 RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
188 a_fnCall; \
189 } \
190 } \
191 } while (0)
192
193
194/** @def FSPERF_VERR_PATH_NOT_FOUND
195 * Hides the fact that we only get VERR_PATH_NOT_FOUND on non-unix systems. */
196#if defined(RT_OS_WINDOWS) //|| defined(RT_OS_OS2) - using posix APIs IIRC, so lost in translation.
197# define FSPERF_VERR_PATH_NOT_FOUND VERR_PATH_NOT_FOUND
198#else
199# define FSPERF_VERR_PATH_NOT_FOUND VERR_FILE_NOT_FOUND
200#endif
201
202
203/*********************************************************************************************************************************
204* Structures and Typedefs *
205*********************************************************************************************************************************/
206typedef struct FSPERFNAMEENTRY
207{
208 RTLISTNODE Entry;
209 uint16_t cchName;
210 char szName[RT_FLEXIBLE_ARRAY];
211} FSPERFNAMEENTRY;
212typedef FSPERFNAMEENTRY *PFSPERFNAMEENTRY;
213
214
215enum
216{
217 kCmdOpt_First = 128,
218
219 kCmdOpt_ManyFiles = kCmdOpt_First,
220 kCmdOpt_NoManyFiles,
221 kCmdOpt_Open,
222 kCmdOpt_NoOpen,
223 kCmdOpt_FStat,
224 kCmdOpt_NoFStat,
225 kCmdOpt_FChMod,
226 kCmdOpt_NoFChMod,
227 kCmdOpt_FUtimes,
228 kCmdOpt_NoFUtimes,
229 kCmdOpt_Stat,
230 kCmdOpt_NoStat,
231 kCmdOpt_ChMod,
232 kCmdOpt_NoChMod,
233 kCmdOpt_Utimes,
234 kCmdOpt_NoUtimes,
235 kCmdOpt_Rename,
236 kCmdOpt_NoRename,
237 kCmdOpt_DirEnum,
238 kCmdOpt_NoDirEnum,
239 kCmdOpt_MkRmDir,
240 kCmdOpt_NoMkRmDir,
241 kCmdOpt_StatVfs,
242 kCmdOpt_NoStatVfs,
243 kCmdOpt_Rm,
244 kCmdOpt_NoRm,
245 kCmdOpt_ChSize,
246 kCmdOpt_NoChSize,
247 kCmdOpt_Read,
248 kCmdOpt_NoRead,
249 kCmdOpt_Write,
250 kCmdOpt_NoWrite,
251 kCmdOpt_Seek,
252 kCmdOpt_NoSeek,
253 kCmdOpt_FSync,
254 kCmdOpt_NoFSync,
255 kCmdOpt_MMap,
256 kCmdOpt_NoMMap,
257
258 kCmdOpt_End
259};
260
261
262/*********************************************************************************************************************************
263* Global Variables *
264*********************************************************************************************************************************/
265/** Command line parameters */
266static const RTGETOPTDEF g_aCmdOptions[] =
267{
268 { "--dir", 'd', RTGETOPT_REQ_STRING },
269 { "--seconds", 's', RTGETOPT_REQ_UINT32 },
270 { "--milliseconds", 'm', RTGETOPT_REQ_UINT64 },
271
272 { "--enable-all", 'e', RTGETOPT_REQ_NOTHING },
273 { "--disable-all", 'z', RTGETOPT_REQ_NOTHING },
274
275 { "--many-files", kCmdOpt_ManyFiles, RTGETOPT_REQ_NOTHING },
276 { "--no-many-files", kCmdOpt_NoManyFiles, RTGETOPT_REQ_NOTHING },
277
278 { "--open", kCmdOpt_Open, RTGETOPT_REQ_NOTHING },
279 { "--no-open", kCmdOpt_NoOpen, RTGETOPT_REQ_NOTHING },
280 { "--fstat", kCmdOpt_FStat, RTGETOPT_REQ_NOTHING },
281 { "--no-fstat", kCmdOpt_NoFStat, RTGETOPT_REQ_NOTHING },
282 { "--fchmod", kCmdOpt_FChMod, RTGETOPT_REQ_NOTHING },
283 { "--no-fchmod", kCmdOpt_NoFChMod, RTGETOPT_REQ_NOTHING },
284 { "--futimes", kCmdOpt_FUtimes, RTGETOPT_REQ_NOTHING },
285 { "--no-futimes", kCmdOpt_NoFUtimes, RTGETOPT_REQ_NOTHING },
286 { "--stat", kCmdOpt_Stat, RTGETOPT_REQ_NOTHING },
287 { "--no-stat", kCmdOpt_NoStat, RTGETOPT_REQ_NOTHING },
288 { "--chmod", kCmdOpt_ChMod, RTGETOPT_REQ_NOTHING },
289 { "--no-chmod", kCmdOpt_NoChMod, RTGETOPT_REQ_NOTHING },
290 { "--utimes", kCmdOpt_Utimes, RTGETOPT_REQ_NOTHING },
291 { "--no-utimes", kCmdOpt_NoUtimes, RTGETOPT_REQ_NOTHING },
292 { "--rename", kCmdOpt_Rename, RTGETOPT_REQ_NOTHING },
293 { "--no-rename", kCmdOpt_NoRename, RTGETOPT_REQ_NOTHING },
294 { "--dir-enum", kCmdOpt_DirEnum, RTGETOPT_REQ_NOTHING },
295 { "--no-dir-enum", kCmdOpt_NoDirEnum, RTGETOPT_REQ_NOTHING },
296 { "--mk-rm-dir", kCmdOpt_MkRmDir, RTGETOPT_REQ_NOTHING },
297 { "--no-mk-rm-dir", kCmdOpt_NoMkRmDir, RTGETOPT_REQ_NOTHING },
298 { "--stat-vfs", kCmdOpt_StatVfs, RTGETOPT_REQ_NOTHING },
299 { "--no-stat-vfs", kCmdOpt_NoStatVfs, RTGETOPT_REQ_NOTHING },
300 { "--rm", kCmdOpt_Rm, RTGETOPT_REQ_NOTHING },
301 { "--no-rm", kCmdOpt_NoRm, RTGETOPT_REQ_NOTHING },
302 { "--chsize", kCmdOpt_ChSize, RTGETOPT_REQ_NOTHING },
303 { "--no-chsize", kCmdOpt_NoChSize, RTGETOPT_REQ_NOTHING },
304 { "--read", kCmdOpt_Read, RTGETOPT_REQ_NOTHING },
305 { "--no-read", kCmdOpt_NoRead, RTGETOPT_REQ_NOTHING },
306 { "--write", kCmdOpt_Write, RTGETOPT_REQ_NOTHING },
307 { "--no-write", kCmdOpt_NoWrite, RTGETOPT_REQ_NOTHING },
308 { "--seek", kCmdOpt_Seek, RTGETOPT_REQ_NOTHING },
309 { "--no-seek", kCmdOpt_NoSeek, RTGETOPT_REQ_NOTHING },
310 { "--fsync", kCmdOpt_FSync, RTGETOPT_REQ_NOTHING },
311 { "--no-fsync", kCmdOpt_NoFSync, RTGETOPT_REQ_NOTHING },
312 { "--mmap", kCmdOpt_MMap, RTGETOPT_REQ_NOTHING },
313 { "--no-mmap", kCmdOpt_NoMMap, RTGETOPT_REQ_NOTHING },
314
315 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
316 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
317 { "--version", 'V', RTGETOPT_REQ_NOTHING },
318 { "--help", 'h', RTGETOPT_REQ_NOTHING } /* for Usage() */
319};
320
321/** The test handle. */
322static RTTEST g_hTest;
323/** The number of nanoseconds a RTTimeNanoTS call takes.
324 * This is used for adjusting loop count estimates. */
325static uint64_t g_nsPerNanoTSCall = 1;
326/** Whether or not to display the duration of each profile run.
327 * This is chiefly for verify the estimate phase. */
328static bool g_fShowDuration = true;
329/** Verbosity level. */
330static uint32_t g_uVerbosity = 0;
331
332/** @name Selected subtest
333 * @{ */
334static bool g_fManyFiles = true;
335static bool g_fOpen = true;
336static bool g_fFStat = true;
337static bool g_fFChMod = true;
338static bool g_fFUtimes = true;
339static bool g_fStat = true;
340static bool g_fChMod = true;
341static bool g_fUtimes = true;
342static bool g_fRename = true;
343static bool g_fDirEnum = true;
344static bool g_fMkRmDir = true;
345static bool g_fStatVfs = true;
346static bool g_fRm = true;
347static bool g_fChSize = true;
348static bool g_fRead = true;
349static bool g_fWrite = true;
350static bool g_fSeek = true;
351static bool g_fFSync = true;
352static bool g_fMMap = true;
353/** @} */
354
355/** The length of each test run. */
356static uint64_t g_nsTestRun = RT_NS_1SEC_64 * 10;
357
358/** For the 'manyfiles' subdir. */
359static uint32_t g_cManyFiles = 10000;
360
361/** Number of files in the 'manytree' directory tree. */
362static uint32_t g_cManyTreeFiles = 640 + 16*640 /*10880*/;
363/** Number of files per directory in the 'manytree' construct. */
364static uint32_t g_cManyTreeFilesPerDir = 640;
365/* Number of subdirs per directory in the 'manytree' construct. */
366static uint32_t g_cManyTreeSubdirsPerDir = 16;
367/** The depth of the 'manytree' directory tree. */
368static uint32_t g_cManyTreeDepth = 1;
369/** List of directories in the many tree, creation order. */
370static RTLISTANCHOR g_ManyTreeHead;
371
372/** Number of configured I/O block sizes. */
373static uint32_t g_cIoBlocks = 8;
374/** Configured I/O block sizes. */
375static uint32_t g_acbIoBlocks[16] = { 1, 512, 4096, 16384, 65536, _1M, _32M, _128M };
376/** The desired size of the test file we use for I/O. */
377static uint64_t g_cbIoFile = _512M;
378
379/** The length of g_szDir. */
380static size_t g_cchDir;
381/** The length of g_szEmptyDir. */
382static size_t g_cchEmptyDir;
383/** The length of g_szDeepDir. */
384static size_t g_cchDeepDir;
385
386/** The test directory (absolute). This will always have a trailing slash. */
387static char g_szDir[RTPATH_MAX];
388/** The empty test directory (absolute). This will always have a trailing slash. */
389static char g_szEmptyDir[RTPATH_MAX];
390/** The deep test directory (absolute). This will always have a trailing slash. */
391static char g_szDeepDir[RTPATH_MAX];
392
393
394/**
395 * Yield the CPU and stuff before starting a test run.
396 */
397DECLINLINE(void) fsPerfYield(void)
398{
399 RTThreadYield();
400 RTThreadYield();
401}
402
403
404/**
405 * Profiles the RTTimeNanoTS call, setting g_nsPerNanoTSCall.
406 */
407static void fsPerfNanoTS(void)
408{
409 fsPerfYield();
410
411 /* Make sure we start off on a changing timestamp on platforms will low time resoultion. */
412 uint64_t nsStart = RTTimeNanoTS();
413 uint64_t ns;
414 do
415 ns = RTTimeNanoTS();
416 while (ns == nsStart);
417 nsStart = ns;
418
419 /* Call it for 10 ms. */
420 uint32_t i = 0;
421 do
422 {
423 i++;
424 ns = RTTimeNanoTS();
425 }
426 while (ns - nsStart < RT_NS_10MS);
427
428 g_nsPerNanoTSCall = (ns - nsStart) / i;
429}
430
431
432/**
433 * Construct a path relative to the base test directory.
434 *
435 * @returns g_szDir.
436 * @param pszAppend What to append.
437 * @param cchAppend How much to append.
438 */
439DECLINLINE(char *) InDir(const char *pszAppend, size_t cchAppend)
440{
441 Assert(g_szDir[g_cchDir - 1] == RTPATH_SLASH);
442 memcpy(&g_szDir[g_cchDir], pszAppend, cchAppend);
443 g_szDir[g_cchDir + cchAppend] = '\0';
444 return &g_szDir[0];
445}
446
447
448/**
449 * Construct a path relative to the empty directory.
450 *
451 * @returns g_szEmptyDir.
452 * @param pszAppend What to append.
453 * @param cchAppend How much to append.
454 */
455DECLINLINE(char *) InEmptyDir(const char *pszAppend, size_t cchAppend)
456{
457 Assert(g_szEmptyDir[g_cchEmptyDir - 1] == RTPATH_SLASH);
458 memcpy(&g_szEmptyDir[g_cchEmptyDir], pszAppend, cchAppend);
459 g_szEmptyDir[g_cchEmptyDir + cchAppend] = '\0';
460 return &g_szEmptyDir[0];
461}
462
463
464/**
465 * Construct a path relative to the deep test directory.
466 *
467 * @returns g_szDeepDir.
468 * @param pszAppend What to append.
469 * @param cchAppend How much to append.
470 */
471DECLINLINE(char *) InDeepDir(const char *pszAppend, size_t cchAppend)
472{
473 Assert(g_szDeepDir[g_cchDeepDir - 1] == RTPATH_SLASH);
474 memcpy(&g_szDeepDir[g_cchDeepDir], pszAppend, cchAppend);
475 g_szDeepDir[g_cchDeepDir + cchAppend] = '\0';
476 return &g_szDeepDir[0];
477}
478
479
480/**
481 * Prepares the test area.
482 * @returns VBox status code.
483 */
484static int fsPrepTestArea(void)
485{
486 /* The empty subdir and associated globals: */
487 static char s_szEmpty[] = "empty";
488 memcpy(g_szEmptyDir, g_szDir, g_cchDir);
489 memcpy(&g_szEmptyDir[g_cchDir], s_szEmpty, sizeof(s_szEmpty));
490 g_cchEmptyDir = g_cchDir + sizeof(s_szEmpty) - 1;
491 RTTESTI_CHECK_RC_RET(RTDirCreate(g_szEmptyDir, 0755, 0), VINF_SUCCESS, rcCheck);
492 g_szEmptyDir[g_cchEmptyDir++] = RTPATH_SLASH;
493 g_szEmptyDir[g_cchEmptyDir] = '\0';
494 RTTestIPrintf(RTTESTLVL_ALWAYS, "Empty dir: %s\n", g_szEmptyDir);
495
496 /* Deep directory: */
497 memcpy(g_szDeepDir, g_szDir, g_cchDir);
498 g_cchDeepDir = g_cchDir;
499 do
500 {
501 static char const s_szSub[] = "d" RTPATH_SLASH_STR;
502 memcpy(&g_szDeepDir[g_cchDeepDir], s_szSub, sizeof(s_szSub));
503 g_cchDeepDir += sizeof(s_szSub) - 1;
504 RTTESTI_CHECK_RC_RET( RTDirCreate(g_szDeepDir, 0755, 0), VINF_SUCCESS, rcCheck);
505 } while (g_cchDeepDir < 176);
506 RTTestIPrintf(RTTESTLVL_ALWAYS, "Deep dir: %s\n", g_szDeepDir);
507
508 /* Create known file in both deep and shallow dirs: */
509 RTFILE hKnownFile;
510 RTTESTI_CHECK_RC_RET(RTFileOpen(&hKnownFile, InDir(RT_STR_TUPLE("known-file")),
511 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE),
512 VINF_SUCCESS, rcCheck);
513 RTTESTI_CHECK_RC_RET(RTFileClose(hKnownFile), VINF_SUCCESS, rcCheck);
514
515 RTTESTI_CHECK_RC_RET(RTFileOpen(&hKnownFile, InDeepDir(RT_STR_TUPLE("known-file")),
516 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE),
517 VINF_SUCCESS, rcCheck);
518 RTTESTI_CHECK_RC_RET(RTFileClose(hKnownFile), VINF_SUCCESS, rcCheck);
519
520 return VINF_SUCCESS;
521}
522
523
524/**
525 * Create a name list entry.
526 * @returns Pointer to the entry, NULL if out of memory.
527 * @param pchName The name.
528 * @param cchName The name length.
529 */
530PFSPERFNAMEENTRY fsPerfCreateNameEntry(const char *pchName, size_t cchName)
531{
532 PFSPERFNAMEENTRY pEntry = (PFSPERFNAMEENTRY)RTMemAllocVar(RT_UOFFSETOF_DYN(FSPERFNAMEENTRY, szName[cchName + 1]));
533 if (pEntry)
534 {
535 RTListInit(&pEntry->Entry);
536 pEntry->cchName = (uint16_t)cchName;
537 memcpy(pEntry->szName, pchName, cchName);
538 pEntry->szName[cchName] = '\0';
539 }
540 return pEntry;
541}
542
543
544static int fsPerfManyTreeRecursiveDirCreator(size_t cchDir, uint32_t iDepth)
545{
546 PFSPERFNAMEENTRY pEntry = fsPerfCreateNameEntry(g_szDir, cchDir);
547 RTTESTI_CHECK_RET(pEntry, VERR_NO_MEMORY);
548 RTListAppend(&g_ManyTreeHead, &pEntry->Entry);
549
550 RTTESTI_CHECK_RC_RET(RTDirCreate(g_szDir, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL),
551 VINF_SUCCESS, rcCheck);
552
553 if (iDepth < g_cManyTreeDepth)
554 for (uint32_t i = 0; i < g_cManyTreeSubdirsPerDir; i++)
555 {
556 size_t cchSubDir = RTStrPrintf(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, "d%02u" RTPATH_SLASH_STR, i);
557 RTTESTI_CHECK_RC_RET(fsPerfManyTreeRecursiveDirCreator(cchDir + cchSubDir, iDepth + 1), VINF_SUCCESS, rcCheck);
558 }
559
560 return VINF_SUCCESS;
561}
562
563
564void fsPerfManyFiles(void)
565{
566 RTTestISub("manyfiles");
567
568 /*
569 * Create a sub-directory with like 10000 files in it.
570 *
571 * This does push the directory organization of the underlying file system,
572 * which is something we might not want to profile with shared folders. It
573 * is however useful for directory enumeration.
574 */
575 RTTESTI_CHECK_RC_RETV(RTDirCreate(InDir(RT_STR_TUPLE("manyfiles")), 0755,
576 RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL),
577 VINF_SUCCESS);
578
579 size_t offFilename = strlen(g_szDir);
580 g_szDir[offFilename++] = RTPATH_SLASH;
581
582 fsPerfYield();
583 RTFILE hFile;
584 uint64_t const nsStart = RTTimeNanoTS();
585 for (uint32_t i = 0; i < g_cManyFiles; i++)
586 {
587 RTStrFormatU32(&g_szDir[offFilename], sizeof(g_szDir) - offFilename, i, 10, 5, 5, RTSTR_F_ZEROPAD);
588 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile, g_szDir, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
589 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
590 }
591 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
592 RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Creating %u empty files in single directory", g_cManyFiles);
593 RTTestIValueF(cNsElapsed / g_cManyFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Create empty file (single dir)");
594
595 /*
596 * Create a bunch of directories with exacly 32 files in each, hoping to
597 * avoid any directory organization artifacts.
598 */
599 /* Create the directories first, building a list of them for simplifying iteration: */
600 RTListInit(&g_ManyTreeHead);
601 InDir(RT_STR_TUPLE("manytree" RTPATH_SLASH_STR));
602 RTTESTI_CHECK_RC_RETV(fsPerfManyTreeRecursiveDirCreator(strlen(g_szDir), 0), VINF_SUCCESS);
603
604 /* Create the zero byte files: */
605 fsPerfYield();
606 uint64_t const nsStart2 = RTTimeNanoTS();
607 uint32_t cFiles = 0;
608 PFSPERFNAMEENTRY pCur;
609 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry)
610 {
611 char szPath[RTPATH_MAX];
612 memcpy(szPath, pCur->szName, pCur->cchName);
613 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++)
614 {
615 RTStrFormatU32(&szPath[pCur->cchName], sizeof(szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD);
616 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile, szPath, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
617 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
618 cFiles++;
619 }
620 }
621 uint64_t const cNsElapsed2 = RTTimeNanoTS() - nsStart2;
622 RTTestIValueF(cNsElapsed2, RTTESTUNIT_NS, "Creating %u empty files in tree", cFiles);
623 RTTestIValueF(cNsElapsed2 / cFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Create empty file (tree)");
624 RTTESTI_CHECK(g_cManyTreeFiles == cFiles);
625}
626
627
628DECL_FORCE_INLINE(int) fsPerfOpenExistingOnceReadonly(const char *pszFile)
629{
630 RTFILE hFile;
631 RTTESTI_CHECK_RC_RET(RTFileOpen(&hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS, rcCheck);
632 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
633 return VINF_SUCCESS;
634}
635
636
637DECL_FORCE_INLINE(int) fsPerfOpenExistingOnceWriteonly(const char *pszFile)
638{
639 RTFILE hFile;
640 RTTESTI_CHECK_RC_RET(RTFileOpen(&hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS, rcCheck);
641 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
642 return VINF_SUCCESS;
643}
644
645
646void fsPerfOpen(void)
647{
648 RTTestISub("open");
649
650 /* Opening non-existing files. */
651 RTFILE hFile;
652 RTTESTI_CHECK_RC(RTFileOpen(&hFile, InEmptyDir(RT_STR_TUPLE("no-such-file")),
653 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VERR_FILE_NOT_FOUND);
654 RTTESTI_CHECK_RC(RTFileOpen(&hFile, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
655 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), FSPERF_VERR_PATH_NOT_FOUND);
656 RTTESTI_CHECK_RC(RTFileOpen(&hFile, InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
657 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VERR_PATH_NOT_FOUND);
658
659 /*
660 * Create file1 and then try exclusivly creating it again.
661 * Then profile opening it for reading.
662 */
663 RTFILE hFile1;
664 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file1")),
665 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
666 RTTESTI_CHECK_RC(RTFileOpen(&hFile, g_szDir, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VERR_ALREADY_EXISTS);
667 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
668
669 PROFILE_FN(fsPerfOpenExistingOnceReadonly(g_szDir), g_nsTestRun, "RTFileOpen/Close/Readonly");
670 PROFILE_FN(fsPerfOpenExistingOnceWriteonly(g_szDir), g_nsTestRun, "RTFileOpen/Close/Writeonly");
671
672 /*
673 * Profile opening in the deep directory too.
674 */
675 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file1")),
676 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
677 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
678 PROFILE_FN(fsPerfOpenExistingOnceReadonly(g_szDeepDir), g_nsTestRun, "RTFileOpen/Close/deep/readonly");
679 PROFILE_FN(fsPerfOpenExistingOnceWriteonly(g_szDeepDir), g_nsTestRun, "RTFileOpen/Close/deep/writeonly");
680
681 /* Manytree: */
682 char szPath[RTPATH_MAX];
683 PROFILE_MANYTREE_FN(szPath, fsPerfOpenExistingOnceReadonly(szPath), 1, g_nsTestRun, "RTFileOpen/Close/manytree/readonly");
684}
685
686
687void fsPerfFStat(void)
688{
689 RTTestISub("fstat");
690 RTFILE hFile1;
691 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file2")),
692 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
693 RTFSOBJINFO ObjInfo = {0};
694 PROFILE_FN(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_NOTHING), g_nsTestRun, "RTFileQueryInfo/NOTHING");
695 PROFILE_FN(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_UNIX), g_nsTestRun, "RTFileQueryInfo/UNIX");
696
697 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
698}
699
700
701void fsPerfFChMod(void)
702{
703 RTTestISub("fchmod");
704 RTFILE hFile1;
705 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file4")),
706 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
707 RTFSOBJINFO ObjInfo = {0};
708 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
709 RTFMODE const fEvenMode = (ObjInfo.Attr.fMode & ~RTFS_UNIX_ALL_ACCESS_PERMS) | RTFS_DOS_READONLY | 0400;
710 RTFMODE const fOddMode = (ObjInfo.Attr.fMode & ~(RTFS_UNIX_ALL_ACCESS_PERMS | RTFS_DOS_READONLY)) | 0640;
711 PROFILE_FN(RTFileSetMode(hFile1, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTFileSetMode");
712
713 RTFileSetMode(hFile1, ObjInfo.Attr.fMode);
714 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
715}
716
717
718void fsPerfFUtimes(void)
719{
720 RTTestISub("futimes");
721 RTFILE hFile1;
722 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file5")),
723 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
724 RTTIMESPEC Time1;
725 RTTimeNow(&Time1);
726 RTTIMESPEC Time2 = Time1;
727 RTTimeSpecSubSeconds(&Time2, 3636);
728
729 RTFSOBJINFO ObjInfo0 = {0};
730 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo0, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
731
732 /* Modify modification time: */
733 RTTESTI_CHECK_RC(RTFileSetTimes(hFile1, NULL, &Time2, NULL, NULL), VINF_SUCCESS);
734 RTFSOBJINFO ObjInfo1 = {0};
735 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo1, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
736 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo1.ModificationTime) >> 2) == (RTTimeSpecGetSeconds(&Time2) >> 2));
737 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo1.AccessTime) == RTTimeSpecGetNano(&ObjInfo0.AccessTime));
738
739 /* Modify access time: */
740 RTTESTI_CHECK_RC(RTFileSetTimes(hFile1, &Time1, NULL, NULL, NULL), VINF_SUCCESS);
741 RTFSOBJINFO ObjInfo2 = {0};
742 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo2, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
743 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo2.AccessTime) >> 2) == (RTTimeSpecGetSeconds(&Time1) >> 2));
744 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo2.ModificationTime) == RTTimeSpecGetNano(&ObjInfo1.ModificationTime));
745
746 /* Benchmark it: */
747 PROFILE_FN(RTFileSetTimes(hFile1, NULL, iIteration & 1 ? &Time1 : &Time2, NULL, NULL), g_nsTestRun, "RTFileSetTimes");
748
749 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
750}
751
752
753void fsPerfStat(void)
754{
755 RTTestISub("stat");
756 RTFSOBJINFO ObjInfo;
757
758 /* Non-existing files. */
759 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InEmptyDir(RT_STR_TUPLE("no-such-file")),
760 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VERR_FILE_NOT_FOUND);
761 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
762 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), FSPERF_VERR_PATH_NOT_FOUND);
763 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
764 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VERR_PATH_NOT_FOUND);
765
766 /* Shallow: */
767 RTFILE hFile1;
768 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file3")),
769 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
770 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
771
772 PROFILE_FN(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), g_nsTestRun,
773 "RTPathQueryInfoEx/NOTHING");
774 PROFILE_FN(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK), g_nsTestRun,
775 "RTPathQueryInfoEx/UNIX");
776
777
778 /* Deep: */
779 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file3")),
780 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
781 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
782
783 PROFILE_FN(RTPathQueryInfoEx(g_szDeepDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), g_nsTestRun,
784 "RTPathQueryInfoEx/deep/NOTHING");
785 PROFILE_FN(RTPathQueryInfoEx(g_szDeepDir, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK), g_nsTestRun,
786 "RTPathQueryInfoEx/deep/UNIX");
787
788 /* Manytree: */
789 char szPath[RTPATH_MAX];
790 PROFILE_MANYTREE_FN(szPath, RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK),
791 1, g_nsTestRun, "RTPathQueryInfoEx/manytree/NOTHING");
792 PROFILE_MANYTREE_FN(szPath, RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK),
793 1, g_nsTestRun, "RTPathQueryInfoEx/manytree/UNIX");
794}
795
796
797void fsPerfChmod(void)
798{
799 RTTestISub("chmod");
800
801 /* Non-existing files. */
802 RTTESTI_CHECK_RC(RTPathSetMode(InEmptyDir(RT_STR_TUPLE("no-such-file")), 0665),
803 VERR_FILE_NOT_FOUND);
804 RTTESTI_CHECK_RC(RTPathSetMode(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), 0665),
805 FSPERF_VERR_PATH_NOT_FOUND);
806 RTTESTI_CHECK_RC(RTPathSetMode(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), 0665), VERR_PATH_NOT_FOUND);
807
808 /* Shallow: */
809 RTFILE hFile1;
810 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file14")),
811 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
812 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
813
814 RTFSOBJINFO ObjInfo;
815 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
816 RTFMODE const fEvenMode = (ObjInfo.Attr.fMode & ~RTFS_UNIX_ALL_ACCESS_PERMS) | RTFS_DOS_READONLY | 0400;
817 RTFMODE const fOddMode = (ObjInfo.Attr.fMode & ~(RTFS_UNIX_ALL_ACCESS_PERMS | RTFS_DOS_READONLY)) | 0640;
818 PROFILE_FN(RTPathSetMode(g_szDir, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTPathSetMode");
819 RTPathSetMode(g_szDir, ObjInfo.Attr.fMode);
820
821 /* Deep: */
822 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file14")),
823 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
824 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
825
826 PROFILE_FN(RTPathSetMode(g_szDeepDir, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTPathSetMode/deep");
827 RTPathSetMode(g_szDeepDir, ObjInfo.Attr.fMode);
828
829 /* Manytree: */
830 char szPath[RTPATH_MAX];
831 PROFILE_MANYTREE_FN(szPath, RTPathSetMode(szPath, iIteration & 1 ? fOddMode : fEvenMode), 1, g_nsTestRun,
832 "RTPathSetMode/manytree");
833 DO_MANYTREE_FN(szPath, RTPathSetMode(szPath, ObjInfo.Attr.fMode));
834}
835
836
837void fsPerfUtimes(void)
838{
839 RTTestISub("utimes");
840
841 RTTIMESPEC Time1;
842 RTTimeNow(&Time1);
843 RTTIMESPEC Time2 = Time1;
844 RTTimeSpecSubSeconds(&Time2, 3636);
845
846 /* Non-existing files. */
847 RTTESTI_CHECK_RC(RTPathSetTimesEx(InEmptyDir(RT_STR_TUPLE("no-such-file")), NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
848 VERR_FILE_NOT_FOUND);
849 RTTESTI_CHECK_RC(RTPathSetTimesEx(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
850 NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
851 FSPERF_VERR_PATH_NOT_FOUND);
852 RTTESTI_CHECK_RC(RTPathSetTimesEx(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
853 NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
854 VERR_PATH_NOT_FOUND);
855
856 /* Shallow: */
857 RTFILE hFile1;
858 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file15")),
859 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
860 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
861
862 RTFSOBJINFO ObjInfo0 = {0};
863 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo0, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
864
865 /* Modify modification time: */
866 RTTESTI_CHECK_RC(RTPathSetTimesEx(g_szDir, NULL, &Time2, NULL, NULL, RTPATH_F_ON_LINK), VINF_SUCCESS);
867 RTFSOBJINFO ObjInfo1;
868 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo1, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
869 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo1.ModificationTime) >> 2) == (RTTimeSpecGetSeconds(&Time2) >> 2));
870 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo1.AccessTime) == RTTimeSpecGetNano(&ObjInfo0.AccessTime));
871
872 /* Modify access time: */
873 RTTESTI_CHECK_RC(RTPathSetTimesEx(g_szDir, &Time1, NULL, NULL, NULL, RTPATH_F_ON_LINK), VINF_SUCCESS);
874 RTFSOBJINFO ObjInfo2 = {0};
875 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo2, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
876 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo2.AccessTime) >> 2) == (RTTimeSpecGetSeconds(&Time1) >> 2));
877 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo2.ModificationTime) == RTTimeSpecGetNano(&ObjInfo1.ModificationTime));
878
879 /* Profile shallow: */
880 PROFILE_FN(RTPathSetTimesEx(g_szDir, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
881 NULL, NULL, RTPATH_F_ON_LINK),
882 g_nsTestRun, "RTPathSetTimesEx");
883
884 /* Deep: */
885 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file15")),
886 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
887 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
888
889 PROFILE_FN(RTPathSetTimesEx(g_szDeepDir, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
890 NULL, NULL, RTPATH_F_ON_LINK),
891 g_nsTestRun, "RTPathSetTimesEx/deep");
892
893 /* Manytree: */
894 char szPath[RTPATH_MAX];
895 PROFILE_MANYTREE_FN(szPath, RTPathSetTimesEx(szPath, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
896 NULL, NULL, RTPATH_F_ON_LINK),
897 1, g_nsTestRun, "RTPathSetTimesEx/manytree");
898}
899
900
901DECL_FORCE_INLINE(int) fsPerfRenameMany(const char *pszFile, uint32_t iIteration)
902{
903 char szRenamed[RTPATH_MAX];
904 strcat(strcpy(szRenamed, pszFile), "-renamed");
905 if (!(iIteration & 1))
906 return RTPathRename(pszFile, szRenamed, 0);
907 return RTPathRename(szRenamed, pszFile, 0);
908}
909
910
911void fsPerfRename(void)
912{
913 RTTestISub("rename");
914 char szPath[RTPATH_MAX];
915
916 /* Non-existing files. */
917 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("other-no-such-file")));
918 RTTESTI_CHECK_RC(RTPathRename(InEmptyDir(RT_STR_TUPLE("no-such-file")), szPath, 0), VERR_FILE_NOT_FOUND);
919 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "other-no-such-file")));
920 RTTESTI_CHECK_RC(RTPathRename(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), szPath, 0),
921 FSPERF_VERR_PATH_NOT_FOUND);
922 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("other-no-such-file")));
923 RTTESTI_CHECK_RC(RTPathRename(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), szPath, 0), VERR_PATH_NOT_FOUND);
924
925 RTFILE hFile1;
926 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file16")),
927 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
928 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
929 strcat(strcpy(szPath, g_szDir), "-no-such-dir" RTPATH_SLASH_STR "file16");
930 RTTESTI_CHECK_RC(RTPathRename(szPath, g_szDir, 0), FSPERF_VERR_PATH_NOT_FOUND);
931 RTTESTI_CHECK_RC(RTPathRename(g_szDir, szPath, 0), FSPERF_VERR_PATH_NOT_FOUND);
932
933 /* Shallow: */
934 strcat(strcpy(szPath, g_szDir), "-other");
935 PROFILE_FN(RTPathRename(iIteration & 1 ? szPath : g_szDir, iIteration & 1 ? g_szDir : szPath, 0), g_nsTestRun, "RTPathRename");
936
937 /* Deep: */
938 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file15")),
939 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
940 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
941
942 strcat(strcpy(szPath, g_szDeepDir), "-other");
943 PROFILE_FN(RTPathRename(iIteration & 1 ? szPath : g_szDeepDir, iIteration & 1 ? g_szDeepDir : szPath, 0),
944 g_nsTestRun, "RTPathRename/deep");
945
946 /* Manytree: */
947 PROFILE_MANYTREE_FN(szPath, fsPerfRenameMany(szPath, iIteration), 2, g_nsTestRun, "RTPathRename/manytree");
948}
949
950
951DECL_FORCE_INLINE(int) fsPerfEnumEmpty(void)
952{
953 RTDIR hDir;
954 g_szEmptyDir[g_cchEmptyDir] = '\0';
955 RTTESTI_CHECK_RC_RET(RTDirOpen(&hDir, g_szEmptyDir), VINF_SUCCESS, rcCheck);
956
957 RTDIRENTRY Entry;
958 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
959 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
960 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
961
962 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
963 return VINF_SUCCESS;
964}
965
966
967DECL_FORCE_INLINE(int) fsPerfEnumManyFiles(void)
968{
969 RTDIR hDir;
970 RTTESTI_CHECK_RC_RET(RTDirOpen(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS, rcCheck);
971 uint32_t cLeft = g_cManyFiles + 2;
972 for (;;)
973 {
974 RTDIRENTRY Entry;
975 if (cLeft > 0)
976 RTTESTI_CHECK_RC_BREAK(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
977 else
978 {
979 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
980 break;
981 }
982 cLeft--;
983 }
984 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
985 return VINF_SUCCESS;
986}
987
988
989void vsPerfDirEnum(void)
990{
991 RTTestISub("dir enum");
992 RTDIR hDir;
993
994 /* Non-existing files. */
995 RTTESTI_CHECK_RC(RTDirOpen(&hDir, InEmptyDir(RT_STR_TUPLE("no-such-file"))), VERR_FILE_NOT_FOUND);
996 RTTESTI_CHECK_RC(RTDirOpen(&hDir, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
997 RTTESTI_CHECK_RC(RTDirOpen(&hDir, InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
998
999 /*
1000 * The empty directory.
1001 */
1002 g_szEmptyDir[g_cchEmptyDir] = '\0';
1003 RTTESTI_CHECK_RC_RETV(RTDirOpen(&hDir, g_szEmptyDir), VINF_SUCCESS);
1004
1005 uint32_t fDots = 0;
1006 RTDIRENTRY Entry;
1007 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
1008 RTTESTI_CHECK(RTDirEntryIsStdDotLink(&Entry));
1009 fDots |= RT_BIT_32(Entry.cbName - 1);
1010
1011 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
1012 RTTESTI_CHECK(RTDirEntryIsStdDotLink(&Entry));
1013 fDots |= RT_BIT_32(Entry.cbName - 1);
1014 RTTESTI_CHECK(fDots == 3);
1015
1016 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
1017
1018 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
1019
1020 /*
1021 * The directory with many files in it.
1022 */
1023 if (g_fManyFiles)
1024 {
1025 fDots = 0;
1026 uint32_t const cBitmap = RT_ALIGN_32(g_cManyFiles, 64);
1027 void *pvBitmap = alloca(cBitmap / 8);
1028 RT_BZERO(pvBitmap, cBitmap / 8);
1029 for (uint32_t i = g_cManyFiles; i < cBitmap; i++)
1030 ASMBitSet(pvBitmap, i);
1031
1032 uint32_t cFiles = 0;
1033 RTTESTI_CHECK_RC_RETV(RTDirOpen(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS);
1034 for (;;)
1035 {
1036 int rc = RTDirRead(hDir, &Entry, NULL);
1037 if (rc == VINF_SUCCESS)
1038 {
1039 if (Entry.szName[0] == '.')
1040 {
1041 if (Entry.szName[1] == '.')
1042 {
1043 RTTESTI_CHECK(!(fDots & 2));
1044 fDots |= 2;
1045 }
1046 else
1047 {
1048 RTTESTI_CHECK(Entry.szName[1] == '\0');
1049 RTTESTI_CHECK(!(fDots & 1));
1050 fDots |= 1;
1051 }
1052 }
1053 else
1054 {
1055 uint32_t iFile = UINT32_MAX;
1056 RTTESTI_CHECK_RC(RTStrToUInt32Full(Entry.szName, 10, &iFile), VINF_SUCCESS);
1057 if ( iFile < g_cManyFiles
1058 && !ASMBitTest(pvBitmap, iFile))
1059 {
1060 ASMBitSet(pvBitmap, iFile);
1061 cFiles++;
1062 }
1063 else
1064 RTTestFailed(g_hTest, "line %u: iFile=%u g_cManyFiles=%u\n", __LINE__, iFile, g_cManyFiles);
1065 }
1066 }
1067 else if (rc == VERR_NO_MORE_FILES)
1068 break;
1069 else
1070 {
1071 RTTestFailed(g_hTest, "RTDirRead failed enumerating manyfiles: %Rrc\n", rc);
1072 RTDirClose(hDir);
1073 return;
1074 }
1075 }
1076 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
1077 RTTESTI_CHECK(fDots == 3);
1078 RTTESTI_CHECK(cFiles == g_cManyFiles);
1079 RTTESTI_CHECK(ASMMemIsAllU8(pvBitmap, cBitmap / 8, 0xff));
1080 }
1081
1082 /*
1083 * Profile.
1084 */
1085 PROFILE_FN(fsPerfEnumEmpty(),g_nsTestRun, "RTDirOpen/Read/Close empty");
1086 if (g_fManyFiles)
1087 PROFILE_FN(fsPerfEnumManyFiles(), g_nsTestRun, "RTDirOpen/Read/Close manyfiles");
1088}
1089
1090
1091void fsPerfMkRmDir(void)
1092{
1093 RTTestISub("mkdir/rmdir");
1094
1095 /* Non-existing directories: */
1096 RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir"))), VERR_FILE_NOT_FOUND);
1097 RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
1098 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
1099 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), 0755, 0), FSPERF_VERR_PATH_NOT_FOUND);
1100 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), 0755, 0), VERR_PATH_NOT_FOUND);
1101
1102 /** @todo check what happens if non-final path component isn't a directory. unix
1103 * should return ENOTDIR and IPRT translates that to VERR_PATH_NOT_FOUND.
1104 * Curious what happens on windows. */
1105
1106 /* Already existing directories and files: */
1107 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE(".")), 0755, 0), VERR_ALREADY_EXISTS);
1108 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE("..")), 0755, 0), VERR_ALREADY_EXISTS);
1109
1110 /* Remove directory with subdirectories: */
1111#if defined(RT_OS_WINDOWS)
1112 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("."))), VERR_DIR_NOT_EMPTY);
1113#else
1114 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("."))), VERR_INVALID_PARAMETER); /* EINVAL for '.' */
1115#endif
1116#if defined(RT_OS_WINDOWS)
1117 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(".."))), VERR_SHARING_VIOLATION); /* weird */
1118#else
1119 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(".."))), VERR_DIR_NOT_EMPTY);
1120#endif
1121 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(""))), VERR_DIR_NOT_EMPTY);
1122
1123 /* Create a directory and remove it: */
1124 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("subdir-1")), 0755, 0), VINF_SUCCESS);
1125 RTTESTI_CHECK_RC(RTDirRemove(g_szDir), VINF_SUCCESS);
1126
1127 /* Create a file and try remove it or create a directory with the same name: */
1128 RTFILE hFile1;
1129 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file18")),
1130 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
1131 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
1132 RTTESTI_CHECK_RC(RTDirRemove(g_szDir), VERR_NOT_A_DIRECTORY);
1133 RTTESTI_CHECK_RC(RTDirCreate(g_szDir, 0755, 0), VERR_ALREADY_EXISTS);
1134 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("file18" RTPATH_SLASH_STR "subdir")), 0755, 0), VERR_PATH_NOT_FOUND);
1135
1136 /*
1137 * Profile alternately creating and removing a bunch of directories.
1138 */
1139 RTTESTI_CHECK_RC_RETV(RTDirCreate(InDir(RT_STR_TUPLE("subdir-2")), 0755, 0), VINF_SUCCESS);
1140 size_t cchDir = strlen(g_szDir);
1141 g_szDir[cchDir++] = RTPATH_SLASH;
1142 g_szDir[cchDir++] = 's';
1143
1144 uint32_t cCreated = 0;
1145 uint64_t nsCreate = 0;
1146 uint64_t nsRemove = 0;
1147 for (;;)
1148 {
1149 /* Create a bunch: */
1150 uint64_t nsStart = RTTimeNanoTS();
1151 for (uint32_t i = 0; i < 998; i++)
1152 {
1153 RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD);
1154 RTTESTI_CHECK_RC_RETV(RTDirCreate(g_szDir, 0755, 0), VINF_SUCCESS);
1155 }
1156 nsCreate += RTTimeNanoTS() - nsStart;
1157 cCreated += 998;
1158
1159 /* Remove the bunch: */
1160 nsStart = RTTimeNanoTS();
1161 for (uint32_t i = 0; i < 998; i++)
1162 {
1163 RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD);
1164 RTTESTI_CHECK_RC_RETV(RTDirRemove(g_szDir), VINF_SUCCESS);
1165 }
1166 nsRemove = RTTimeNanoTS() - nsStart;
1167
1168 /* Check if we got time for another round: */
1169 if ( ( nsRemove >= g_nsTestRun
1170 && nsCreate >= g_nsTestRun)
1171 || nsCreate + nsRemove >= g_nsTestRun * 3)
1172 break;
1173 }
1174 RTTestIValue("RTDirCreate", nsCreate / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE);
1175 RTTestIValue("RTDirRemove", nsRemove / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE);
1176}
1177
1178
1179void fsPerfStatVfs(void)
1180{
1181 RTTestISub("statvfs");
1182
1183 g_szEmptyDir[g_cchEmptyDir] = '\0';
1184 RTFOFF cbTotal;
1185 RTFOFF cbFree;
1186 uint32_t cbBlock;
1187 uint32_t cbSector;
1188 RTTESTI_CHECK_RC(RTFsQuerySizes(g_szEmptyDir, &cbTotal, &cbFree, &cbBlock, &cbSector), VINF_SUCCESS);
1189
1190 uint32_t uSerial;
1191 RTTESTI_CHECK_RC(RTFsQuerySerial(g_szEmptyDir, &uSerial), VINF_SUCCESS);
1192
1193 RTFSPROPERTIES Props;
1194 RTTESTI_CHECK_RC(RTFsQueryProperties(g_szEmptyDir, &Props), VINF_SUCCESS);
1195
1196 RTFSTYPE enmType;
1197 RTTESTI_CHECK_RC(RTFsQueryType(g_szEmptyDir, &enmType), VINF_SUCCESS);
1198
1199}
1200
1201
1202void fsPerfRm(void)
1203{
1204 RTTestISub("rm");
1205
1206 /* Non-existing files. */
1207 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-file"))), VERR_FILE_NOT_FOUND);
1208 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
1209 RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
1210
1211 /* Directories: */
1212 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("."))), VERR_ACCESS_DENIED);
1213 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(".."))), VERR_ACCESS_DENIED);
1214 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(""))), VERR_ACCESS_DENIED);
1215
1216 /* Shallow: */
1217 RTFILE hFile1;
1218 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file19")),
1219 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
1220 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
1221 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
1222 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VERR_FILE_NOT_FOUND);
1223
1224 if (g_fManyFiles)
1225 {
1226 /*
1227 * Profile the deletion of the manyfiles content.
1228 */
1229 {
1230 InDir(RT_STR_TUPLE("manyfiles" RTPATH_SLASH_STR));
1231 size_t const offFilename = strlen(g_szDir);
1232 fsPerfYield();
1233 uint64_t const nsStart = RTTimeNanoTS();
1234 for (uint32_t i = 0; i < g_cManyFiles; i++)
1235 {
1236 RTStrFormatU32(&g_szDir[offFilename], sizeof(g_szDir) - offFilename, i, 10, 5, 5, RTSTR_F_ZEROPAD);
1237 RTTESTI_CHECK_RC_RETV(RTFileDelete(g_szDir), VINF_SUCCESS);
1238 }
1239 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
1240 RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Deleted %u empty files from a single directory", g_cManyFiles);
1241 RTTestIValueF(cNsElapsed / g_cManyFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Delete file (single dir)");
1242 }
1243
1244 /*
1245 * Ditto for the manytree.
1246 */
1247 {
1248 char szPath[RTPATH_MAX];
1249 uint64_t const nsStart = RTTimeNanoTS();
1250 DO_MANYTREE_FN(szPath, RTTESTI_CHECK_RC_RETV(RTFileDelete(szPath), VINF_SUCCESS));
1251 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
1252 RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Deleted %u empty files in tree", g_cManyTreeFiles);
1253 RTTestIValueF(cNsElapsed / g_cManyTreeFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Delete file (tree)");
1254 }
1255 }
1256}
1257
1258
1259void fsPerfChSize(void)
1260{
1261 RTTestISub("chsize");
1262
1263 /*
1264 * We need some free space to perform this test.
1265 */
1266 g_szDir[g_cchDir] = '\0';
1267 RTFOFF cbFree = 0;
1268 RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
1269 if (cbFree < _1M)
1270 {
1271 RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 1MB", cbFree);
1272 return;
1273 }
1274
1275 /*
1276 * Create a file and play around with it's size.
1277 * We let the current file position follow the end position as we make changes.
1278 */
1279 RTFILE hFile1;
1280 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file20")),
1281 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
1282 uint64_t cbFile = UINT64_MAX;
1283 RTTESTI_CHECK_RC(RTFileGetSize(hFile1, &cbFile), VINF_SUCCESS);
1284 RTTESTI_CHECK(cbFile == 0);
1285
1286 uint8_t abBuf[4096];
1287 static uint64_t const s_acbChanges[] =
1288 {
1289 1023, 1024, 1024, 1025, 8192, 11111, _1M, _8M, _8M,
1290 _4M, _2M + 1, _1M - 1, 65537, 65536, 32768, 8000, 7999, 7998, 1024, 1, 0
1291 };
1292 uint64_t cbOld = 0;
1293 for (unsigned i = 0; i < RT_ELEMENTS(s_acbChanges); i++)
1294 {
1295 uint64_t cbNew = s_acbChanges[i];
1296 if (cbNew + _64K >= (uint64_t)cbFree)
1297 continue;
1298
1299 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, cbNew), VINF_SUCCESS);
1300 RTTESTI_CHECK_RC(RTFileGetSize(hFile1, &cbFile), VINF_SUCCESS);
1301 RTTESTI_CHECK_MSG(cbFile == cbNew, ("cbFile=%#RX64 cbNew=%#RX64\n", cbFile, cbNew));
1302
1303 if (cbNew > cbOld)
1304 {
1305 /* Check that the extension is all zeroed: */
1306 uint64_t cbLeft = cbNew - cbOld;
1307 while (cbLeft > 0)
1308 {
1309 memset(abBuf, 0xff, sizeof(abBuf));
1310 size_t cbToRead = sizeof(abBuf);
1311 if (cbToRead > cbLeft)
1312 cbToRead = (size_t)cbLeft;
1313 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, cbToRead, NULL), VINF_SUCCESS);
1314 RTTESTI_CHECK(ASMMemIsZero(abBuf, cbToRead));
1315 cbLeft -= cbToRead;
1316 }
1317 }
1318 else
1319 {
1320 /* Check that reading fails with EOF because current position is now beyond the end: */
1321 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 1, NULL), VERR_EOF);
1322
1323 /* Keep current position at the end of the file: */
1324 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbNew, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1325 }
1326 cbOld = cbNew;
1327 }
1328
1329 /*
1330 * Profile just the file setting operation itself, keeping the changes within
1331 * an allocation unit to avoid needing to adjust the actual (host) FS allocation.
1332 * ASSUMES allocation unit >= 512 and power of two.
1333 */
1334 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, _64K), VINF_SUCCESS);
1335 PROFILE_FN(RTFileSetSize(hFile1, _64K - (iIteration & 255) - 128), g_nsTestRun, "RTFileSetSize/noalloc");
1336
1337 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS);
1338 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
1339 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
1340}
1341
1342
1343int fsPerfIoPrepFile(RTFILE hFile1, uint64_t cbFile, uint8_t **ppbFree)
1344{
1345 /*
1346 * Seek to the end - 4K and write the last 4K.
1347 * This should have the effect of filling the whole file with zeros.
1348 */
1349 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, cbFile - _4K, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1350 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, g_abRTZero4K, _4K, NULL), VINF_SUCCESS, rcCheck);
1351
1352 /*
1353 * Check that the space we searched across actually is zero filled.
1354 */
1355 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1356 size_t cbBuf = _1M;
1357 uint8_t *pbBuf = *ppbFree = (uint8_t *)RTMemAlloc(cbBuf);
1358 RTTESTI_CHECK_RET(pbBuf != NULL, VERR_NO_MEMORY);
1359 uint64_t cbLeft = cbFile;
1360 while (cbLeft > 0)
1361 {
1362 size_t cbToRead = cbBuf;
1363 if (cbToRead > cbLeft)
1364 cbToRead = (size_t)cbLeft;
1365 pbBuf[cbToRead] = 0xff;
1366
1367 RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBuf, cbToRead, NULL), VINF_SUCCESS, rcCheck);
1368 RTTESTI_CHECK_RET(ASMMemIsZero(pbBuf, cbToRead), VERR_MISMATCH);
1369
1370 cbLeft -= cbToRead;
1371 }
1372
1373 /*
1374 * Fill the file with 0xf6 and insert offset markers with 1KB intervals.
1375 */
1376 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1377 memset(pbBuf, 0xf6, cbBuf);
1378 cbLeft = cbFile;
1379 uint64_t off = 0;
1380 while (cbLeft > 0)
1381 {
1382 Assert(!(off & (_1K - 1)));
1383 Assert(!(cbBuf & (_1K - 1)));
1384 for (size_t offBuf = 0; offBuf < cbBuf; offBuf += _1K, off += _1K)
1385 *(uint64_t *)&pbBuf[offBuf] = off;
1386
1387 size_t cbToWrite = cbBuf;
1388 if (cbToWrite > cbLeft)
1389 cbToWrite = (size_t)cbLeft;
1390
1391 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBuf, cbToWrite, NULL), VINF_SUCCESS, rcCheck);
1392
1393 cbLeft -= cbToWrite;
1394 }
1395
1396 return VINF_SUCCESS;
1397}
1398
1399
1400/**
1401 * Checks the content read from the file fsPerfIoPrepFile() prepared.
1402 */
1403bool fsPerfCheckReadBuf(unsigned uLineNo, uint64_t off, uint8_t const *pbBuf, size_t cbBuf, uint8_t bFiller = 0xf6)
1404{
1405 uint32_t cMismatches = 0;
1406 size_t offBuf = 0;
1407 uint32_t offBlock = (uint32_t)(off & (_1K - 1));
1408 while (offBuf < cbBuf)
1409 {
1410 /*
1411 * Check the offset marker:
1412 */
1413 if (offBlock < sizeof(uint64_t))
1414 {
1415 RTUINT64U uMarker;
1416 uMarker.u = off + offBuf - offBlock;
1417 unsigned offMarker = offBlock & (sizeof(uint64_t) - 1);
1418 while (offMarker < sizeof(uint64_t) && offBuf < cbBuf)
1419 {
1420 if (uMarker.au8[offMarker] != pbBuf[offBuf])
1421 {
1422 RTTestIFailed("%u: Mismatch at buffer/file offset %#zx/%#RX64: %#x, expected %#x",
1423 uLineNo, offBuf, off + offBuf, pbBuf[offBuf], uMarker.au8[offMarker]);
1424 if (cMismatches++ > 32)
1425 return false;
1426 }
1427 offMarker++;
1428 offBuf++;
1429 }
1430 offBlock = sizeof(uint64_t);
1431 }
1432
1433 /*
1434 * Check the filling:
1435 */
1436 size_t cbFilling = RT_MIN(_1K - offBlock, cbBuf - offBuf);
1437 if ( cbFilling == 0
1438 || ASMMemIsAllU8(&pbBuf[offBuf], cbFilling, bFiller))
1439 offBuf += cbFilling;
1440 else
1441 {
1442 /* Some mismatch, locate it/them: */
1443 while (cbFilling > 0 && offBuf < cbBuf)
1444 {
1445 if (pbBuf[offBuf] != bFiller)
1446 {
1447 RTTestIFailed("%u: Mismatch at buffer/file offset %#zx/%#RX64: %#x, expected %#04x",
1448 uLineNo, offBuf, off + offBuf, pbBuf[offBuf], bFiller);
1449 if (cMismatches++ > 32)
1450 return false;
1451 }
1452 offBuf++;
1453 cbFilling--;
1454 }
1455 }
1456 offBlock = 0;
1457 }
1458 return cMismatches == 0;
1459}
1460
1461
1462/**
1463 * Sets up write buffer with offset markers and fillers.
1464 */
1465void fsPerfFillWriteBuf(uint64_t off, uint8_t *pbBuf, size_t cbBuf, uint8_t bFiller = 0xf6)
1466{
1467 uint32_t offBlock = (uint32_t)(off & (_1K - 1));
1468 while (cbBuf > 0)
1469 {
1470 /* The marker. */
1471 if (offBlock < sizeof(uint64_t))
1472 {
1473 RTUINT64U uMarker;
1474 uMarker.u = off + offBlock;
1475 if (cbBuf > sizeof(uMarker) - offBlock)
1476 {
1477 memcpy(pbBuf, &uMarker.au8[offBlock], sizeof(uMarker) - offBlock);
1478 pbBuf += sizeof(uMarker) - offBlock;
1479 cbBuf -= sizeof(uMarker) - offBlock;
1480 off += sizeof(uMarker) - offBlock;
1481 }
1482 else
1483 {
1484 memcpy(pbBuf, &uMarker.au8[offBlock], cbBuf);
1485 return;
1486 }
1487 offBlock = sizeof(uint64_t);
1488 }
1489
1490 /* Do the filling. */
1491 size_t cbFilling = RT_MIN(_1K - offBlock, cbBuf);
1492 memset(pbBuf, bFiller, cbFilling);
1493 pbBuf += cbFilling;
1494 cbBuf -= cbFilling;
1495 off += cbFilling;
1496
1497 offBlock = 0;
1498 }
1499}
1500
1501
1502
1503void fsPerfIoSeek(RTFILE hFile1, uint64_t cbFile)
1504{
1505 /*
1506 * Do a bunch of search tests, most which are random.
1507 */
1508 struct
1509 {
1510 int rc;
1511 uint32_t uMethod;
1512 int64_t offSeek;
1513 uint64_t offActual;
1514
1515 } aSeeks[9 + 64] =
1516 {
1517 { VINF_SUCCESS, RTFILE_SEEK_BEGIN, 0, 0 },
1518 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 },
1519 { VINF_SUCCESS, RTFILE_SEEK_END, 0, cbFile },
1520 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -4096, cbFile - 4096 },
1521 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 4096 - (int64_t)cbFile, 0 },
1522 { VINF_SUCCESS, RTFILE_SEEK_END, -(int64_t)cbFile/2, cbFile / 2 + (cbFile & 1) },
1523 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -(int64_t)cbFile/2, 0 },
1524#if defined(RT_OS_WINDOWS)
1525 { VERR_NEGATIVE_SEEK, RTFILE_SEEK_CURRENT, -1, 0 },
1526#else
1527 { VERR_INVALID_PARAMETER, RTFILE_SEEK_CURRENT, -1, 0 },
1528#endif
1529 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 },
1530 };
1531
1532 uint64_t offActual = 0;
1533 for (unsigned i = 9; i < RT_ELEMENTS(aSeeks); i++)
1534 {
1535 switch (RTRandU32Ex(RTFILE_SEEK_BEGIN, RTFILE_SEEK_END))
1536 {
1537 default: AssertFailedBreak();
1538 case RTFILE_SEEK_BEGIN:
1539 aSeeks[i].uMethod = RTFILE_SEEK_BEGIN;
1540 aSeeks[i].rc = VINF_SUCCESS;
1541 aSeeks[i].offSeek = RTRandU64Ex(0, cbFile + cbFile / 8);
1542 aSeeks[i].offActual = offActual = aSeeks[i].offSeek;
1543 break;
1544
1545 case RTFILE_SEEK_CURRENT:
1546 aSeeks[i].uMethod = RTFILE_SEEK_CURRENT;
1547 aSeeks[i].rc = VINF_SUCCESS;
1548 aSeeks[i].offSeek = (int64_t)RTRandU64Ex(0, cbFile + cbFile / 8) - (int64_t)offActual;
1549 aSeeks[i].offActual = offActual += aSeeks[i].offSeek;
1550 break;
1551
1552 case RTFILE_SEEK_END:
1553 aSeeks[i].uMethod = RTFILE_SEEK_END;
1554 aSeeks[i].rc = VINF_SUCCESS;
1555 aSeeks[i].offSeek = -(int64_t)RTRandU64Ex(0, cbFile);
1556 aSeeks[i].offActual = offActual = cbFile + aSeeks[i].offSeek;
1557 break;
1558 }
1559 }
1560
1561 for (unsigned iDoReadCheck = 0; iDoReadCheck < 2; iDoReadCheck++)
1562 {
1563 for (uint32_t i = 0; i < RT_ELEMENTS(aSeeks); i++)
1564 {
1565 offActual = UINT64_MAX;
1566 int rc = RTFileSeek(hFile1, aSeeks[i].offSeek, aSeeks[i].uMethod, &offActual);
1567 if (rc != aSeeks[i].rc)
1568 RTTestIFailed("Seek #%u: Expected %Rrc, got %Rrc", i, aSeeks[i].rc, rc);
1569 if (RT_SUCCESS(rc) && offActual != aSeeks[i].offActual)
1570 RTTestIFailed("Seek #%u: offActual %#RX64, expected %#RX64", i, offActual, aSeeks[i].offActual);
1571 if (RT_SUCCESS(rc))
1572 {
1573 uint64_t offTell = RTFileTell(hFile1);
1574 if (offTell != offActual)
1575 RTTestIFailed("Seek #%u: offActual %#RX64, RTFileTell %#RX64", i, offActual, offTell);
1576 }
1577
1578 if (RT_SUCCESS(rc) && offActual + _2K <= cbFile && iDoReadCheck)
1579 {
1580 uint8_t abBuf[_2K];
1581 RTTESTI_CHECK_RC(rc = RTFileRead(hFile1, abBuf, sizeof(abBuf), NULL), VINF_SUCCESS);
1582 if (RT_SUCCESS(rc))
1583 {
1584 size_t offMarker = (size_t)(RT_ALIGN_64(offActual, _1K) - offActual);
1585 uint64_t uMarker = *(uint64_t *)&abBuf[offMarker]; /** @todo potentially unaligned access */
1586 if (uMarker != offActual + offMarker)
1587 RTTestIFailed("Seek #%u: Invalid marker value (@ %#RX64): %#RX64, expected %#RX64",
1588 i, offActual, uMarker, offActual + offMarker);
1589
1590 RTTESTI_CHECK_RC(RTFileSeek(hFile1, -(int64_t)sizeof(abBuf), RTFILE_SEEK_CURRENT, NULL), VINF_SUCCESS);
1591 }
1592 }
1593 }
1594 }
1595
1596
1597 /*
1598 * Profile seeking relative to the beginning of the file and relative
1599 * to the end. The latter might be more expensive in a SF context.
1600 */
1601 PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? iIteration : iIteration % cbFile, RTFILE_SEEK_BEGIN, NULL),
1602 g_nsTestRun, "RTFileSeek/BEGIN");
1603 PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? -(int64_t)iIteration : -(int64_t)(iIteration % cbFile), RTFILE_SEEK_END, NULL),
1604 g_nsTestRun, "RTFileSeek/END");
1605
1606}
1607
1608
1609/** For fsPerfIoRead and fsPerfIoWrite. */
1610#define PROFILE_IO_FN(a_szOperation, a_fnCall) \
1611 do \
1612 { \
1613 RTTESTI_CHECK_RC_RETV(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS); \
1614 uint64_t offActual = 0; \
1615 uint32_t cSeeks = 0; \
1616 \
1617 /* Estimate how many iterations we need to fill up the given timeslot: */ \
1618 fsPerfYield(); \
1619 uint64_t nsStart = RTTimeNanoTS(); \
1620 uint64_t ns; \
1621 do \
1622 ns = RTTimeNanoTS(); \
1623 while (ns == nsStart); \
1624 nsStart = ns; \
1625 \
1626 uint64_t iIteration = 0; \
1627 do \
1628 { \
1629 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
1630 iIteration++; \
1631 ns = RTTimeNanoTS() - nsStart; \
1632 } while (ns < RT_NS_10MS); \
1633 ns /= iIteration; \
1634 if (ns > g_nsPerNanoTSCall + 32) \
1635 ns -= g_nsPerNanoTSCall; \
1636 uint64_t cIterations = g_nsTestRun / ns; \
1637 \
1638 /* Do the actual profiling: */ \
1639 cSeeks = 0; \
1640 iIteration = 0; \
1641 fsPerfYield(); \
1642 nsStart = RTTimeNanoTS(); \
1643 for (uint32_t iAdjust = 0; iAdjust < 4; iAdjust++) \
1644 { \
1645 for (; iIteration < cIterations; iIteration++)\
1646 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
1647 ns = RTTimeNanoTS() - nsStart;\
1648 if (ns >= g_nsTestRun - (g_nsTestRun / 10)) \
1649 break; \
1650 cIterations += cIterations / 4; \
1651 if (cIterations & 1) \
1652 cIterations++; \
1653 nsStart += g_nsPerNanoTSCall; \
1654 } \
1655 RTTestIValueF(ns / iIteration, \
1656 RTTESTUNIT_NS_PER_OCCURRENCE, a_szOperation "/seq/%RU32 latency", cbBlock); \
1657 RTTestIValueF((uint64_t)iIteration * cbBlock / ((double)ns / RT_NS_1SEC), \
1658 RTTESTUNIT_BYTES_PER_SEC, a_szOperation "/seq/%RU32 throughput", cbBlock); \
1659 RTTestIValueF(iIteration, \
1660 RTTESTUNIT_CALLS, a_szOperation "/seq/%RU32 calls", cbBlock); \
1661 RTTestIValueF((uint64_t)iIteration * cbBlock, \
1662 RTTESTUNIT_BYTES, a_szOperation "/seq/%RU32 bytes", cbBlock); \
1663 RTTestIValueF(cSeeks, \
1664 RTTESTUNIT_OCCURRENCES, a_szOperation "/seq/%RU32 seeks", cbBlock); \
1665 if (g_fShowDuration) \
1666 RTTestIValueF(ns, RTTESTUNIT_NS, a_szOperation "/seq/%RU32 duration", cbBlock); \
1667 } while (0)
1668
1669
1670/**
1671 * One RTFileRead profiling iteration.
1672 */
1673DECL_FORCE_INLINE(int) fsPerfIoReadWorker(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t *pbBlock,
1674 uint64_t *poffActual, uint32_t *pcSeeks)
1675{
1676 /* Do we need to seek back to the start? */
1677 if (*poffActual + cbBlock <= cbFile)
1678 { /* likely */ }
1679 else
1680 {
1681 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1682 *pcSeeks += 1;
1683 *poffActual = 0;
1684 }
1685
1686 size_t cbActuallyRead = 0;
1687 RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBlock, cbBlock, &cbActuallyRead), VINF_SUCCESS, rcCheck);
1688 if (cbActuallyRead == cbBlock)
1689 {
1690 *poffActual += cbActuallyRead;
1691 return VINF_SUCCESS;
1692 }
1693 RTTestIFailed("RTFileRead at %#RX64 returned just %#x bytes, expected %#x", *poffActual, cbActuallyRead, cbBlock);
1694 *poffActual += cbActuallyRead;
1695 return VERR_READ_ERROR;
1696}
1697
1698
1699void fsPerfIoReadBlockSize(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock)
1700{
1701 RTTestISubF("IO - Sequential read %RU32", cbBlock);
1702
1703 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBlock);
1704 if (pbBuf)
1705 {
1706 memset(pbBuf, 0xf7, cbBlock);
1707 PROFILE_IO_FN("RTFileRead", fsPerfIoReadWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks));
1708 RTMemPageFree(pbBuf, cbBlock);
1709 }
1710 else
1711 RTTestSkipped(g_hTest, "insufficient (virtual) memory available");
1712}
1713
1714
1715void fsPerfRead(RTFILE hFile1, RTFILE hFileNoCache, uint64_t cbFile)
1716{
1717 RTTestISubF("IO - RTFileRead");
1718
1719 /*
1720 * Allocate a big buffer we can play around with. Min size is 1MB.
1721 */
1722 size_t cbBuf = cbFile < _64M ? (size_t)cbFile : _64M;
1723 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
1724 while (!pbBuf)
1725 {
1726 cbBuf /= 2;
1727 RTTESTI_CHECK_RETV(cbBuf >= _1M);
1728 pbBuf = (uint8_t *)RTMemPageAlloc(_32M);
1729 }
1730
1731#if 1
1732 /*
1733 * Start at the beginning and read the full buffer in random small chunks, thereby
1734 * checking that unaligned buffer addresses, size and file offsets work fine.
1735 */
1736 struct
1737 {
1738 uint64_t offFile;
1739 uint32_t cbMax;
1740 } aRuns[] = { { 0, 127 }, { cbFile - cbBuf, UINT32_MAX }, { 0, UINT32_MAX -1 }};
1741 for (uint32_t i = 0; i < RT_ELEMENTS(aRuns); i++)
1742 {
1743 memset(pbBuf, 0x55, cbBuf);
1744 RTTESTI_CHECK_RC(RTFileSeek(hFile1, aRuns[i].offFile, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1745 for (size_t offBuf = 0; offBuf < cbBuf; )
1746 {
1747 uint32_t const cbLeft = (uint32_t)(cbBuf - offBuf);
1748 uint32_t const cbToRead = aRuns[i].cbMax < UINT32_MAX / 2 ? RTRandU32Ex(1, aRuns[i].cbMax)
1749 : aRuns[i].cbMax == UINT32_MAX ? RTRandU32Ex(RT_MAX(cbLeft / 4, 1), cbLeft)
1750 : RTRandU32Ex(cbLeft >= _8K ? _8K : 1, RT_MIN(_1M, cbLeft));
1751 size_t cbActual = 0;
1752 RTTESTI_CHECK_RC(RTFileRead(hFile1, &pbBuf[offBuf], cbToRead, &cbActual), VINF_SUCCESS);
1753 if (cbActual == cbToRead)
1754 offBuf += cbActual;
1755 else
1756 {
1757 RTTestIFailed("Attempting to read %#x bytes at %#zx, only got %#x bytes back!\n", cbToRead, offBuf, cbActual);
1758 if (cbActual)
1759 offBuf += cbActual;
1760 else
1761 pbBuf[offBuf++] = 0x11;
1762 }
1763 }
1764 fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf);
1765 }
1766#endif
1767
1768 /*
1769 * Test reading beyond the end of the file.
1770 */
1771 size_t const acbMax[] = { cbBuf, _64K, _16K, _4K, 256 };
1772 uint32_t const aoffFromEos[] =
1773 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32, 63, 64, 127, 128, 255, 254, 256, 1023, 1024, 2048,
1774 4092, 4093, 4094, 4095, 4096, 4097, 4098, 4099, 4100, 8192, 16384, 32767, 32768, 32769, 65535, 65536, _1M - 1
1775 };
1776 for (unsigned iMax = 0; iMax < RT_ELEMENTS(acbMax); iMax++)
1777 {
1778 size_t const cbMaxRead = acbMax[iMax];
1779 for (uint32_t iOffFromEos = 0; iOffFromEos < RT_ELEMENTS(aoffFromEos); iOffFromEos++)
1780 {
1781 uint32_t off = aoffFromEos[iOffFromEos];
1782 if (off >= cbMaxRead)
1783 continue;
1784 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile - off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1785 size_t cbActual = ~(size_t)0;
1786 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, &cbActual), VINF_SUCCESS);
1787 RTTESTI_CHECK(cbActual == off);
1788
1789 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile - off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1790 cbActual = ~(size_t)0;
1791 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, off, &cbActual), VINF_SUCCESS);
1792 RTTESTI_CHECK_MSG(cbActual == off, ("%#zx vs %#zx", cbActual, off));
1793
1794 cbActual = ~(size_t)0;
1795 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, 1, &cbActual), VINF_SUCCESS);
1796 RTTESTI_CHECK(cbActual == 0);
1797
1798 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, NULL), VERR_EOF);
1799 }
1800 }
1801
1802 /*
1803 * Test reading beyond end of the file.
1804 */
1805 for (unsigned iMax = 0; iMax < RT_ELEMENTS(acbMax); iMax++)
1806 {
1807 size_t const cbMaxRead = acbMax[iMax];
1808 for (uint32_t off = 0; off < 256; off++)
1809 {
1810 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile + off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1811 size_t cbActual = ~(size_t)0;
1812 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, &cbActual), VINF_SUCCESS);
1813 RTTESTI_CHECK(cbActual == 0);
1814
1815 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, NULL), VERR_EOF);
1816 }
1817 }
1818
1819 /*
1820 * Do uncached access, must be page aligned.
1821 */
1822 uint32_t cbPage = PAGE_SIZE;
1823 memset(pbBuf, 0x66, cbBuf);
1824 RTTESTI_CHECK_RC(RTFileSeek(hFileNoCache, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1825 for (size_t offBuf = 0; offBuf < cbBuf; )
1826 {
1827 uint32_t const cPagesLeft = (uint32_t)((cbBuf - offBuf) / cbPage);
1828 uint32_t const cPagesToRead = RTRandU32Ex(1, cPagesLeft);
1829 size_t const cbToRead = cPagesToRead * (size_t)cbPage;
1830 size_t cbActual = 0;
1831 RTTESTI_CHECK_RC(RTFileRead(hFileNoCache, &pbBuf[offBuf], cbToRead, &cbActual), VINF_SUCCESS);
1832 if (cbActual == cbToRead)
1833 offBuf += cbActual;
1834 else
1835 {
1836 RTTestIFailed("Attempting to read %#zx bytes at %#zx, only got %#x bytes back!\n", cbToRead, offBuf, cbActual);
1837 if (cbActual)
1838 offBuf += cbActual;
1839 else
1840 {
1841 memset(&pbBuf[offBuf], 0x11, cbPage);
1842 offBuf += cbPage;
1843 }
1844 }
1845 }
1846 fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf);
1847
1848 /*
1849 * Check reading zero bytes at the end of the file.
1850 * Requires native call because RTFileWrite doesn't call kernel on zero byte reads.
1851 */
1852 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
1853#ifdef RT_OS_WINDOWS
1854 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1855 NTSTATUS rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 0, NULL, NULL);
1856 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
1857 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
1858 RTTESTI_CHECK(Ios.Information == 0);
1859
1860 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1861 rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 1, NULL, NULL);
1862 RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE, ("rcNt=%#x", rcNt));
1863 RTTESTI_CHECK(Ios.Status == STATUS_END_OF_FILE);
1864 RTTESTI_CHECK(Ios.Information == 0);
1865#endif
1866
1867 /*
1868 * Other OS specific stuff.
1869 */
1870#ifdef RT_OS_WINDOWS
1871 /* Check that reading at an offset modifies the position: */
1872 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
1873 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile);
1874
1875 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1876 LARGE_INTEGER offNt;
1877 offNt.QuadPart = cbFile / 2;
1878 rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, _4K, &offNt, NULL);
1879 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
1880 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
1881 RTTESTI_CHECK(Ios.Information == _4K);
1882 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile / 2 + _4K);
1883 fsPerfCheckReadBuf(__LINE__, cbFile / 2, pbBuf, _4K);
1884#endif
1885
1886 RTMemPageFree(pbBuf, cbBuf);
1887}
1888
1889
1890/**
1891 * One RTFileWrite profiling iteration.
1892 */
1893DECL_FORCE_INLINE(int) fsPerfIoWriteWorker(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t *pbBlock,
1894 uint64_t *poffActual, uint32_t *pcSeeks)
1895{
1896 /* Do we need to seek back to the start? */
1897 if (*poffActual + cbBlock <= cbFile)
1898 { /* likely */ }
1899 else
1900 {
1901 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1902 *pcSeeks += 1;
1903 *poffActual = 0;
1904 }
1905
1906 size_t cbActuallyWritten = 0;
1907 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBlock, cbBlock, &cbActuallyWritten), VINF_SUCCESS, rcCheck);
1908 if (cbActuallyWritten == cbBlock)
1909 {
1910 *poffActual += cbActuallyWritten;
1911 return VINF_SUCCESS;
1912 }
1913 RTTestIFailed("RTFileWrite at %#RX64 returned just %#x bytes, expected %#x", *poffActual, cbActuallyWritten, cbBlock);
1914 *poffActual += cbActuallyWritten;
1915 return VERR_WRITE_ERROR;
1916}
1917
1918
1919void fsPerfIoWriteBlockSize(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock)
1920{
1921 RTTestISubF("IO - Sequential write %RU32", cbBlock);
1922
1923 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBlock);
1924 if (pbBuf)
1925 {
1926 memset(pbBuf, 0xf7, cbBlock);
1927 PROFILE_IO_FN("RTFileWrite", fsPerfIoWriteWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks));
1928 RTMemPageFree(pbBuf, cbBlock);
1929 }
1930 else
1931 RTTestSkipped(g_hTest, "insufficient (virtual) memory available");
1932}
1933
1934
1935void fsPerfWrite(RTFILE hFile1, RTFILE hFileNoCache, RTFILE hFileWriteThru, uint64_t cbFile)
1936{
1937 RTTestISubF("IO - RTFileWrite");
1938
1939 /*
1940 * Allocate a big buffer we can play around with. Min size is 1MB.
1941 */
1942 size_t cbBuf = cbFile < _64M ? (size_t)cbFile : _64M;
1943 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
1944 while (!pbBuf)
1945 {
1946 cbBuf /= 2;
1947 RTTESTI_CHECK_RETV(cbBuf >= _1M);
1948 pbBuf = (uint8_t *)RTMemPageAlloc(_32M);
1949 }
1950
1951 /*
1952 * Start at the beginning and write out the full buffer in random small chunks, thereby
1953 * checking that unaligned buffer addresses, size and file offsets work fine.
1954 */
1955 struct
1956 {
1957 uint64_t offFile;
1958 uint32_t cbMax;
1959 } aRuns[] = { { 0, 127 }, { cbFile - cbBuf, UINT32_MAX }, { 0, UINT32_MAX -1 }};
1960 uint8_t bFiller = 0x88;
1961 for (uint32_t i = 0; i < RT_ELEMENTS(aRuns); i++, bFiller)
1962 {
1963 fsPerfFillWriteBuf(aRuns[i].offFile, pbBuf, cbBuf, bFiller);
1964 fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf, bFiller);
1965
1966 RTTESTI_CHECK_RC(RTFileSeek(hFile1, aRuns[i].offFile, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1967 for (size_t offBuf = 0; offBuf < cbBuf; )
1968 {
1969 uint32_t const cbLeft = (uint32_t)(cbBuf - offBuf);
1970 uint32_t const cbToWrite = aRuns[i].cbMax < UINT32_MAX / 2 ? RTRandU32Ex(1, aRuns[i].cbMax)
1971 : aRuns[i].cbMax == UINT32_MAX ? RTRandU32Ex(RT_MAX(cbLeft / 4, 1), cbLeft)
1972 : RTRandU32Ex(cbLeft >= _8K ? _8K : 1, RT_MIN(_1M, cbLeft));
1973 size_t cbActual = 0;
1974 RTTESTI_CHECK_RC(RTFileWrite(hFile1, &pbBuf[offBuf], cbToWrite, &cbActual), VINF_SUCCESS);
1975 if (cbActual == cbToWrite)
1976 offBuf += cbActual;
1977 else
1978 {
1979 RTTestIFailed("Attempting to write %#x bytes at %#zx, only got %#x written!\n", cbToWrite, offBuf, cbActual);
1980 if (cbActual)
1981 offBuf += cbActual;
1982 else
1983 pbBuf[offBuf++] = 0x11;
1984 }
1985 }
1986
1987 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, aRuns[i].offFile, pbBuf, cbBuf, NULL), VINF_SUCCESS);
1988 fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf, bFiller);
1989 }
1990
1991
1992 /*
1993 * Do uncached and write-thru accesses, must be page aligned.
1994 */
1995 RTFILE ahFiles[2] = { hFileWriteThru, hFileNoCache };
1996 for (unsigned iFile = 0; iFile < RT_ELEMENTS(ahFiles); iFile++, bFiller++)
1997 {
1998 fsPerfFillWriteBuf(0, pbBuf, cbBuf, bFiller);
1999 fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf, bFiller);
2000 RTTESTI_CHECK_RC(RTFileSeek(ahFiles[iFile], 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
2001
2002 uint32_t cbPage = PAGE_SIZE;
2003 for (size_t offBuf = 0; offBuf < cbBuf; )
2004 {
2005 uint32_t const cPagesLeft = (uint32_t)((cbBuf - offBuf) / cbPage);
2006 uint32_t const cPagesToWrite = RTRandU32Ex(1, cPagesLeft);
2007 size_t const cbToWrite = cPagesToWrite * (size_t)cbPage;
2008 size_t cbActual = 0;
2009 RTTESTI_CHECK_RC(RTFileWrite(ahFiles[iFile], &pbBuf[offBuf], cbToWrite, &cbActual), VINF_SUCCESS);
2010 if (cbActual == cbToWrite)
2011 {
2012 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, offBuf, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
2013 fsPerfCheckReadBuf(__LINE__, offBuf, pbBuf, cbToWrite, bFiller);
2014 offBuf += cbActual;
2015 }
2016 else
2017 {
2018 RTTestIFailed("Attempting to read %#zx bytes at %#zx, only got %#x written!\n", cbToWrite, offBuf, cbActual);
2019 if (cbActual)
2020 offBuf += cbActual;
2021 else
2022 {
2023 memset(&pbBuf[offBuf], 0x11, cbPage);
2024 offBuf += cbPage;
2025 }
2026 }
2027 }
2028
2029 RTTESTI_CHECK_RC(RTFileReadAt(ahFiles[iFile], 0, pbBuf, cbBuf, NULL), VINF_SUCCESS);
2030 fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf, bFiller);
2031 }
2032
2033 /*
2034 * Check the behavior of writing zero bytes to the file _4K from the end
2035 * using native API. In the olden days zero sized write have been known
2036 * to be used to truncate a file.
2037 */
2038 RTTESTI_CHECK_RC(RTFileSeek(hFile1, -_4K, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
2039#ifdef RT_OS_WINDOWS
2040 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2041 NTSTATUS rcNt = NtWriteFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 0, NULL, NULL);
2042 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
2043 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
2044 RTTESTI_CHECK(Ios.Information == 0);
2045
2046 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, _4K, NULL), VINF_SUCCESS);
2047 fsPerfCheckReadBuf(__LINE__, cbFile - _4K, pbBuf, _4K, pbBuf[0x8]);
2048#endif
2049
2050 /*
2051 * Other OS specific stuff.
2052 */
2053#ifdef RT_OS_WINDOWS
2054 /* Check that reading at an offset modifies the position: */
2055 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, cbFile / 2, pbBuf, _4K, NULL), VINF_SUCCESS);
2056 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
2057 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile);
2058
2059 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
2060 LARGE_INTEGER offNt;
2061 offNt.QuadPart = cbFile / 2;
2062 rcNt = NtWriteFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, _4K, &offNt, NULL);
2063 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
2064 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
2065 RTTESTI_CHECK(Ios.Information == _4K);
2066 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile / 2 + _4K);
2067#endif
2068
2069 RT_NOREF(hFileNoCache, hFileWriteThru);
2070 RTMemPageFree(pbBuf, cbBuf);
2071}
2072
2073
2074/**
2075 * Worker for testing RTFileFlush.
2076 */
2077DECL_FORCE_INLINE(int) fsPerfFSyncWorker(RTFILE hFile1, uint64_t cbFile, uint8_t *pbBuf, size_t cbBuf, uint64_t *poffFile)
2078{
2079 if (*poffFile + cbBuf <= cbFile)
2080 { /* likely */ }
2081 else
2082 {
2083 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
2084 *poffFile = 0;
2085 }
2086
2087 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBuf, cbBuf, NULL), VINF_SUCCESS, rcCheck);
2088 RTTESTI_CHECK_RC_RET(RTFileFlush(hFile1), VINF_SUCCESS, rcCheck);
2089
2090 *poffFile += cbBuf;
2091 return VINF_SUCCESS;
2092}
2093
2094
2095void fsPerfFSync(RTFILE hFile1, uint64_t cbFile)
2096{
2097 RTTestISub("fsync");
2098
2099 RTTESTI_CHECK_RC(RTFileFlush(hFile1), VINF_SUCCESS);
2100
2101 PROFILE_FN(RTFileFlush(hFile1), g_nsTestRun, "RTFileFlush");
2102
2103 size_t cbBuf = PAGE_SIZE;
2104 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
2105 RTTESTI_CHECK_RETV(pbBuf != NULL);
2106 memset(pbBuf, 0xf4, cbBuf);
2107
2108 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
2109 uint64_t offFile = 0;
2110 PROFILE_FN(fsPerfFSyncWorker(hFile1, cbFile, pbBuf, cbBuf, &offFile), g_nsTestRun, "RTFileWrite[Page]/RTFileFlush");
2111
2112 RTMemPageFree(pbBuf, cbBuf);
2113}
2114
2115
2116void fsPerfMMap(RTFILE hFile1, RTFILE hFileNoCache, uint64_t cbFile)
2117{
2118 RTTestISub("mmap");
2119#if defined(RT_OS_WINDOWS)
2120 static const char * const s_apszStates[] = { "readonly", "writecopy", "readwrite" };
2121 enum { kMMap_ReadOnly = 0, kMMap_WriteCopy, kMMap_ReadWrite, kMMap_End };
2122 for (int enmState = kMMap_ReadOnly; enmState < kMMap_End; enmState++)
2123 {
2124 /*
2125 * Do the mapping.
2126 */
2127 size_t cbMapping = (size_t)cbFile;
2128 if (cbMapping != cbFile)
2129 cbMapping = _256M;
2130 uint8_t *pbMapping;
2131
2132# ifdef RT_OS_WINDOWS
2133 HANDLE hSection;
2134 for (;; cbMapping /= 2)
2135 {
2136 hSection = CreateFileMapping((HANDLE)RTFileToNative(hFile1), NULL,
2137 enmState == kMMap_ReadOnly ? PAGE_READONLY
2138 : enmState == kMMap_WriteCopy ? PAGE_WRITECOPY : PAGE_READWRITE,
2139 (uint32_t)((uint64_t)cbMapping >> 32), (uint32_t)cbMapping, NULL);
2140 DWORD dwErr1 = GetLastError();
2141 DWORD dwErr2 = 0;
2142 if (hSection != NULL)
2143 {
2144 pbMapping = (uint8_t *)MapViewOfFile(hSection,
2145 enmState == kMMap_ReadOnly ? FILE_MAP_READ
2146 : enmState == kMMap_WriteCopy ? FILE_MAP_COPY
2147 : FILE_MAP_WRITE,
2148 0, 0, cbMapping);
2149 if (pbMapping)
2150 break;
2151 dwErr2 = GetLastError();
2152 CloseHandle(hSection);
2153 }
2154 if (cbMapping <= _2M)
2155 {
2156 RTTestIFailed("%u: CreateFileMapping or MapViewOfFile failed: %u, %u", enmState, dwErr1, dwErr2);
2157 return;
2158 }
2159 }
2160# else
2161 for (;; cbMapping /= 2)
2162 {
2163 pbMapping = (uint8_t *)mmap(NULL, cbMapping,
2164 enmState == kMMap_ReadOnly ? PROT_READ : PROT_READ | PROT_WRITE,
2165 enmState == kMMap_WriteCopy ? MAP_PRIVATE : MAP_SHARED,
2166 (int)RTFileToNative(hFile1), cbMapping);
2167 if ((void *)pbMapping != MAP_FAILED)
2168 break;
2169 RTTESTI_CHECK_MSG_RETV(cbMapping > _2M, ("errno=%d", errno));
2170 }
2171# endif
2172
2173 /*
2174 * Time page-ins just for fun.
2175 */
2176 size_t const cPages = cbMapping >> PAGE_SHIFT;
2177 size_t uDummy = 0;
2178 uint64_t ns = RTTimeNanoTS();
2179 for (size_t iPage = 0; iPage < cPages; iPage++)
2180 uDummy += ASMAtomicReadU8(&pbMapping[iPage << PAGE_SHIFT]);
2181 ns = RTTimeNanoTS() - ns;
2182 RTTestIValueF(ns / cPages, RTTESTUNIT_NS_PER_OCCURRENCE, "page-in %s", s_apszStates[enmState]);
2183
2184 /* Check the content. */
2185 fsPerfCheckReadBuf(__LINE__, 0, pbMapping, cbMapping);
2186
2187 if (enmState != kMMap_ReadOnly)
2188 {
2189 /* Write stuff to the first two megabytes. In the COW case, we'll detect
2190 corruption of shared data during content checking of the RW iterations. */
2191 fsPerfFillWriteBuf(0, pbMapping, _2M, 0xf7);
2192 if (enmState == kMMap_ReadWrite)
2193 {
2194 /* For RW we can try read back from the file handle and check if we get
2195 a match there first. */
2196 uint8_t abBuf[_4K];
2197 for (uint32_t off = 0; off < _2M; off += sizeof(abBuf))
2198 {
2199 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, off, abBuf, sizeof(abBuf), NULL), VINF_SUCCESS);
2200 fsPerfCheckReadBuf(__LINE__, off, abBuf, sizeof(abBuf), 0xf7);
2201 }
2202# ifdef RT_OS_WINDOWS
2203 RTTESTI_CHECK(FlushViewOfFile(pbMapping, _2M));
2204# else
2205 RTTESTI_CHECK(msync(pbMapping, _2M, MS_SYNC) == 0);
2206# endif
2207
2208 /*
2209 * Time modifying and flushing a few different number of pages.
2210 */
2211 static size_t const s_acbFlush[] = { PAGE_SIZE, PAGE_SIZE * 2, PAGE_SIZE * 3, PAGE_SIZE * 8, PAGE_SIZE * 16, _2M };
2212 for (unsigned iFlushSize = 0 ; iFlushSize < RT_ELEMENTS(s_acbFlush); iFlushSize++)
2213 {
2214 size_t const cbFlush = s_acbFlush[iFlushSize];
2215 if (cbFlush > cbMapping)
2216 continue;
2217
2218 size_t const cFlushes = cbMapping / cbFlush;
2219 uint8_t *pbCur = pbMapping;
2220 ns = RTTimeNanoTS();
2221 for (size_t iFlush = 0; iFlush < cFlushes; iFlush++, pbCur += cbFlush)
2222 {
2223 for (size_t offFlush = 0; offFlush < cbFlush; offFlush += PAGE_SIZE)
2224 *(size_t volatile *)&pbCur[offFlush + 8] = cbFlush;
2225# ifdef RT_OS_WINDOWS
2226 RTTESTI_CHECK(FlushViewOfFile(pbCur, cbFlush));
2227# else
2228 RTTESTI_CHECK(msync(pbCur, cbFlush, MS_SYNC) == 0);
2229# endif
2230 }
2231 ns = RTTimeNanoTS() - ns;
2232 RTTestIValueF(ns / cFlushes, RTTESTUNIT_NS_PER_OCCURRENCE, "touch/flush/%zu", cbFlush);
2233
2234 /*
2235 * Check that all the changes made it thru to the file:
2236 */
2237 size_t cbBuf = _2M;
2238 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(_2M);
2239 if (!pbBuf)
2240 {
2241 cbBuf = _4K;
2242 pbBuf = (uint8_t *)RTMemPageAlloc(_2M);
2243 }
2244 if (pbBuf)
2245 {
2246 RTTESTI_CHECK_RC(RTFileSeek(hFileNoCache, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
2247 size_t const cbToCheck = cFlushes * cbFlush;
2248 unsigned cErrors = 0;
2249 for (size_t offBuf = 0; cErrors < 32 && offBuf < cbToCheck; offBuf += cbBuf)
2250 {
2251 size_t cbToRead = RT_MIN(cbBuf, cbToCheck - offBuf);
2252 RTTESTI_CHECK_RC(RTFileRead(hFileNoCache, pbBuf, cbToRead, NULL), VINF_SUCCESS);
2253
2254 for (size_t offFlush = 0; offFlush < cbToRead; offFlush += PAGE_SIZE)
2255 if (*(size_t volatile *)&pbBuf[offFlush + 8] != cbFlush)
2256 {
2257 RTTestIFailed("Flush issue at offset #%zx: %#zx, expected %#zx (cbFlush=%#zx)",
2258 offBuf, *(size_t volatile *)&pbBuf[offFlush + 8], cbFlush, cbFlush);
2259 if (++cErrors > 32)
2260 break;
2261 }
2262 }
2263 RTMemPageFree(pbBuf, cbBuf);
2264 }
2265 }
2266 }
2267 }
2268
2269 /*
2270 * Unmap it.
2271 */
2272# ifdef RT_OS_WINDOWS
2273 RTTESTI_CHECK(UnmapViewOfFile(pbMapping));
2274 RTTESTI_CHECK(CloseHandle(hSection));
2275# else
2276 RTTESTI_CHECK(munmap(pbMapping, cbMapping) == 0);
2277# endif
2278 }
2279
2280 RT_NOREF(hFileNoCache);
2281#else
2282 RTTestSkipped(g_hTest, "not supported/implemented");
2283 RT_NOREF(hFile1, hFileNoCache, cbFile);
2284#endif
2285}
2286
2287
2288/**
2289 * This does the read, write and seek tests.
2290 */
2291void fsPerfIo(void)
2292{
2293 RTTestISub("I/O");
2294
2295 /*
2296 * Determin the size of the test file.
2297 */
2298 g_szDir[g_cchDir] = '\0';
2299 RTFOFF cbFree = 0;
2300 RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
2301 uint64_t cbFile = g_cbIoFile;
2302 if (cbFile + _16M < (uint64_t)cbFree)
2303 cbFile = RT_ALIGN_64(cbFile, _64K);
2304 else
2305 {
2306 if (cbFree < _32M)
2307 {
2308 RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 32MB", cbFree);
2309 return;
2310 }
2311 cbFile = cbFree - (cbFree > _128M ? _64M : _16M);
2312 cbFile = RT_ALIGN_64(cbFile, _64K);
2313 RTTestIPrintf(RTTESTLVL_ALWAYS, "Adjusted file size to %'RU64 bytes, due to %'RU64 bytes free.\n", cbFile, cbFree);
2314 }
2315 if (cbFile < _64K)
2316 {
2317 RTTestSkipped(g_hTest, "Specified test file size too small: %'RU64 bytes, requires >= 64KB", cbFile);
2318 return;
2319 }
2320
2321 /*
2322 * Create a cbFile sized test file.
2323 */
2324 RTFILE hFile1;
2325 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file21")),
2326 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
2327 RTFILE hFileNoCache;
2328 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFileNoCache, g_szDir,
2329 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE | RTFILE_O_NO_CACHE),
2330 VINF_SUCCESS);
2331 RTFILE hFileWriteThru;
2332 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFileWriteThru, g_szDir,
2333 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE | RTFILE_O_WRITE_THROUGH),
2334 VINF_SUCCESS);
2335
2336 uint8_t *pbFree = NULL;
2337 int rc = fsPerfIoPrepFile(hFile1, cbFile, &pbFree);
2338 RTMemFree(pbFree);
2339 if (RT_SUCCESS(rc))
2340 {
2341 /*
2342 * Do the testing & profiling.
2343 */
2344 if (g_fSeek)
2345 fsPerfIoSeek(hFile1, cbFile);
2346 if (g_fRead)
2347 {
2348 fsPerfRead(hFile1, hFileNoCache, cbFile);
2349 for (unsigned i = 0; i < g_cIoBlocks; i++)
2350 fsPerfIoReadBlockSize(hFile1, cbFile, g_acbIoBlocks[i]);
2351 }
2352 if (g_fMMap)
2353 fsPerfMMap(hFile1, hFileNoCache, cbFile);
2354 if (g_fWrite)
2355 {
2356 /* This is destructive to the file content. */
2357 fsPerfWrite(hFile1, hFileNoCache, hFileWriteThru, cbFile);
2358 for (unsigned i = 0; i < g_cIoBlocks; i++)
2359 fsPerfIoWriteBlockSize(hFile1, cbFile, g_acbIoBlocks[i]);
2360 }
2361 if (g_fFSync)
2362 fsPerfFSync(hFile1, cbFile);
2363 }
2364
2365 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS);
2366 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2367 RTTESTI_CHECK_RC(RTFileClose(hFileNoCache), VINF_SUCCESS);
2368 RTTESTI_CHECK_RC(RTFileClose(hFileWriteThru), VINF_SUCCESS);
2369 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
2370}
2371
2372
2373static void Usage(PRTSTREAM pStrm)
2374{
2375 char szExec[RTPATH_MAX];
2376 RTStrmPrintf(pStrm, "usage: %s <-d <testdir>> [options]\n",
2377 RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
2378 RTStrmPrintf(pStrm, "\n");
2379 RTStrmPrintf(pStrm, "options: \n");
2380
2381 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
2382 {
2383 char szHelp[80];
2384 const char *pszHelp;
2385 switch (g_aCmdOptions[i].iShort)
2386 {
2387 case 'd': pszHelp = "The directory to use for testing. Default: CWD/fstestdir"; break;
2388 case 'e': pszHelp = "Enables all tests. Default: -e"; break;
2389 case 'z': pszHelp = "Disables all tests. Default: -e"; break;
2390 case 's': pszHelp = "Set benchmark duration in seconds. Default: 10 sec"; break;
2391 case 'm': pszHelp = "Set benchmark duration in milliseconds. Default: 10000 ms"; break;
2392 case 'v': pszHelp = "More verbose execution."; break;
2393 case 'q': pszHelp = "Quiet execution."; break;
2394 case 'h': pszHelp = "Displays this help and exit"; break;
2395 case 'V': pszHelp = "Displays the program revision"; break;
2396 default:
2397 if (g_aCmdOptions[i].iShort >= kCmdOpt_First)
2398 {
2399 if (RTStrStartsWith(g_aCmdOptions[i].pszLong, "--no-"))
2400 RTStrPrintf(szHelp, sizeof(szHelp), "Disables the '%s' test.", g_aCmdOptions[i].pszLong + 5);
2401 else
2402 RTStrPrintf(szHelp, sizeof(szHelp), "Enables the '%s' test.", g_aCmdOptions[i].pszLong + 2);
2403 pszHelp = szHelp;
2404 }
2405 else
2406 pszHelp = "Option undocumented";
2407 break;
2408 }
2409 if ((unsigned)g_aCmdOptions[i].iShort < 127U)
2410 {
2411 char szOpt[64];
2412 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
2413 RTStrmPrintf(pStrm, " %-20s%s\n", szOpt, pszHelp);
2414 }
2415 else
2416 RTStrmPrintf(pStrm, " %-20s%s\n", g_aCmdOptions[i].pszLong, pszHelp);
2417 }
2418}
2419
2420
2421int main(int argc, char *argv[])
2422{
2423 /*
2424 * Init IPRT and globals.
2425 */
2426 int rc = RTTestInitAndCreate("FsPerf", &g_hTest);
2427 if (rc)
2428 return rc;
2429 RTListInit(&g_ManyTreeHead);
2430
2431 /*
2432 * Default values.
2433 */
2434 rc = RTPathGetCurrent(g_szDir, sizeof(g_szDir) / 2);
2435 if (RT_SUCCESS(rc))
2436 rc = RTPathAppend(g_szDir, sizeof(g_szDir) / 2, "fstestdir-");
2437 if (RT_SUCCESS(rc))
2438 {
2439 g_cchDir = strlen(g_szDir);
2440 g_cchDir += RTStrPrintf(&g_szDir[g_cchDir], sizeof(g_szDir) - g_cchDir, "%u" RTPATH_SLASH_STR, RTProcSelf());
2441 }
2442 else
2443 {
2444 RTTestFailed(g_hTest, "RTPathGetCurrent (or RTPathAppend) failed: %Rrc\n", rc);
2445 return RTTestSummaryAndDestroy(g_hTest);
2446 }
2447
2448 RTGETOPTUNION ValueUnion;
2449 RTGETOPTSTATE GetState;
2450 RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
2451 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2452 {
2453 switch (rc)
2454 {
2455 case 'd':
2456 rc = RTPathAbs(ValueUnion.psz, g_szDir, sizeof(g_szDir) / 2);
2457 if (RT_SUCCESS(rc))
2458 break;
2459 RTTestFailed(g_hTest, "RTPathAbs(%s) failed: %Rrc\n", ValueUnion.psz, rc);
2460 return RTTestSummaryAndDestroy(g_hTest);
2461
2462 case 's':
2463 if (ValueUnion.u32 == 0)
2464 g_nsTestRun = RT_NS_1SEC_64 * 10;
2465 else
2466 g_nsTestRun = ValueUnion.u32 * RT_NS_1SEC_64;
2467 break;
2468
2469 case 'm':
2470 if (ValueUnion.u64 == 0)
2471 g_nsTestRun = RT_NS_1SEC_64 * 10;
2472 else
2473 g_nsTestRun = ValueUnion.u64 * RT_NS_1MS;
2474 break;
2475
2476 case 'e':
2477 g_fManyFiles = true;
2478 g_fOpen = true;
2479 g_fFStat = true;
2480 g_fFChMod = true;
2481 g_fFUtimes = true;
2482 g_fStat = true;
2483 g_fChMod = true;
2484 g_fUtimes = true;
2485 g_fRename = true;
2486 g_fDirEnum = true;
2487 g_fMkRmDir = true;
2488 g_fStatVfs = true;
2489 g_fRm = true;
2490 g_fChSize = true;
2491 g_fRead = true;
2492 g_fWrite = true;
2493 g_fSeek = true;
2494 g_fFSync = true;
2495 g_fMMap = true;
2496 break;
2497
2498 case 'z':
2499 g_fManyFiles = false;
2500 g_fOpen = false;
2501 g_fFStat = false;
2502 g_fFChMod = false;
2503 g_fFUtimes = false;
2504 g_fStat = false;
2505 g_fChMod = false;
2506 g_fUtimes = false;
2507 g_fRename = false;
2508 g_fDirEnum = false;
2509 g_fMkRmDir = false;
2510 g_fStatVfs = false;
2511 g_fRm = false;
2512 g_fChSize = false;
2513 g_fRead = false;
2514 g_fWrite = false;
2515 g_fSeek = false;
2516 g_fFSync = false;
2517 g_fMMap = false;
2518 break;
2519
2520#define CASE_OPT(a_Stem) \
2521 case RT_CONCAT(kCmdOpt_,a_Stem): RT_CONCAT(g_f,a_Stem) = true; break; \
2522 case RT_CONCAT(kCmdOpt_No,a_Stem): RT_CONCAT(g_f,a_Stem) = false; break
2523 CASE_OPT(ManyFiles);
2524 CASE_OPT(Open);
2525 CASE_OPT(FStat);
2526 CASE_OPT(FChMod);
2527 CASE_OPT(FUtimes);
2528 CASE_OPT(Stat);
2529 CASE_OPT(ChMod);
2530 CASE_OPT(Utimes);
2531 CASE_OPT(Rename);
2532 CASE_OPT(DirEnum);
2533 CASE_OPT(MkRmDir);
2534 CASE_OPT(StatVfs);
2535 CASE_OPT(Rm);
2536 CASE_OPT(ChSize);
2537 CASE_OPT(Read);
2538 CASE_OPT(Write);
2539 CASE_OPT(Seek);
2540 CASE_OPT(FSync);
2541 CASE_OPT(MMap);
2542#undef CASE_OPT
2543
2544 case 'q':
2545 g_uVerbosity = 0;
2546 break;
2547
2548 case 'v':
2549 g_uVerbosity++;
2550 break;
2551
2552 case 'h':
2553 Usage(g_pStdOut);
2554 return RTEXITCODE_SUCCESS;
2555
2556 case 'V':
2557 {
2558 char szRev[] = "$Revision: 76917 $";
2559 szRev[RT_ELEMENTS(szRev) - 2] = '\0';
2560 RTPrintf(RTStrStrip(strchr(szRev, ':') + 1));
2561 return RTEXITCODE_SUCCESS;
2562 }
2563
2564 default:
2565 return RTGetOptPrintError(rc, &ValueUnion);
2566 }
2567 }
2568
2569 /*
2570 * Create the test directory with an 'empty' subdirectory under it,
2571 * execute the tests, and remove directory when done.
2572 */
2573 RTTestBanner(g_hTest);
2574 if (!RTPathExists(g_szDir))
2575 {
2576 /* The base dir: */
2577 rc = RTDirCreate(g_szDir, 0755,
2578 RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
2579 if (RT_SUCCESS(rc))
2580 {
2581 RTTestIPrintf(RTTESTLVL_ALWAYS, "Test dir: %s\n", g_szDir);
2582 rc = fsPrepTestArea();
2583 if (RT_SUCCESS(rc))
2584 {
2585 /* Profile RTTimeNanoTS(). */
2586 fsPerfNanoTS();
2587
2588 /* Do tests: */
2589 if (g_fManyFiles)
2590 fsPerfManyFiles();
2591 if (g_fOpen)
2592 fsPerfOpen();
2593 if (g_fFStat)
2594 fsPerfFStat();
2595 if (g_fFChMod)
2596 fsPerfFChMod();
2597 if (g_fFUtimes)
2598 fsPerfFUtimes();
2599 if (g_fStat)
2600 fsPerfStat();
2601 if (g_fChMod)
2602 fsPerfChmod();
2603 if (g_fUtimes)
2604 fsPerfUtimes();
2605 if (g_fRename)
2606 fsPerfRename();
2607 if (g_fDirEnum)
2608 vsPerfDirEnum();
2609 if (g_fMkRmDir)
2610 fsPerfMkRmDir();
2611 if (g_fStatVfs)
2612 fsPerfStatVfs();
2613 if (g_fRm || g_fManyFiles)
2614 fsPerfRm(); /* deletes manyfiles and manytree */
2615 if (g_fChSize)
2616 fsPerfChSize();
2617 if (g_fRead || g_fWrite || g_fSeek || g_fFSync || g_fMMap)
2618 fsPerfIo();
2619 }
2620
2621 /* Cleanup: */
2622 g_szDir[g_cchDir] = '\0';
2623 rc = RTDirRemoveRecursive(g_szDir, RTDIRRMREC_F_CONTENT_AND_DIR);
2624 if (RT_FAILURE(rc))
2625 RTTestFailed(g_hTest, "RTDirRemoveRecursive(%s,) -> %Rrc\n", g_szDir, rc);
2626 }
2627 else
2628 RTTestFailed(g_hTest, "RTDirCreate(%s) -> %Rrc\n", g_szDir, rc);
2629 }
2630 else
2631 RTTestFailed(g_hTest, "Test directory already exists: %s\n", g_szDir);
2632
2633 return RTTestSummaryAndDestroy(g_hTest);
2634}
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