VirtualBox

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

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

FsPerf: Linux updates. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 102.4 KB
Line 
1/* $Id: FsPerf.cpp 76918 2019-01-21 13:11:50Z 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 char sz1[RTTIME_STR_LEN], sz2[RTTIME_STR_LEN]; /* Div by 1000 here for posix impl. using timeval. */
738 RTTESTI_CHECK_MSG(RTTimeSpecGetNano(&ObjInfo1.AccessTime) / 1000 == RTTimeSpecGetNano(&ObjInfo0.AccessTime) / 1000,
739 ("%s, expected %s", RTTimeSpecToString(&ObjInfo1.AccessTime, sz1, sizeof(sz1)),
740 RTTimeSpecToString(&ObjInfo0.AccessTime, sz2, sizeof(sz2))));
741
742 /* Modify access time: */
743 RTTESTI_CHECK_RC(RTFileSetTimes(hFile1, &Time1, NULL, NULL, NULL), VINF_SUCCESS);
744 RTFSOBJINFO ObjInfo2 = {0};
745 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo2, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
746 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo2.AccessTime) >> 2) == (RTTimeSpecGetSeconds(&Time1) >> 2));
747 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo2.ModificationTime) / 1000 == RTTimeSpecGetNano(&ObjInfo1.ModificationTime) / 1000);
748
749 /* Benchmark it: */
750 PROFILE_FN(RTFileSetTimes(hFile1, NULL, iIteration & 1 ? &Time1 : &Time2, NULL, NULL), g_nsTestRun, "RTFileSetTimes");
751
752 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
753}
754
755
756void fsPerfStat(void)
757{
758 RTTestISub("stat");
759 RTFSOBJINFO ObjInfo;
760
761 /* Non-existing files. */
762 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InEmptyDir(RT_STR_TUPLE("no-such-file")),
763 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VERR_FILE_NOT_FOUND);
764 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
765 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), FSPERF_VERR_PATH_NOT_FOUND);
766 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
767 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VERR_PATH_NOT_FOUND);
768
769 /* Shallow: */
770 RTFILE hFile1;
771 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file3")),
772 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
773 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
774
775 PROFILE_FN(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), g_nsTestRun,
776 "RTPathQueryInfoEx/NOTHING");
777 PROFILE_FN(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK), g_nsTestRun,
778 "RTPathQueryInfoEx/UNIX");
779
780
781 /* Deep: */
782 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file3")),
783 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
784 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
785
786 PROFILE_FN(RTPathQueryInfoEx(g_szDeepDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), g_nsTestRun,
787 "RTPathQueryInfoEx/deep/NOTHING");
788 PROFILE_FN(RTPathQueryInfoEx(g_szDeepDir, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK), g_nsTestRun,
789 "RTPathQueryInfoEx/deep/UNIX");
790
791 /* Manytree: */
792 char szPath[RTPATH_MAX];
793 PROFILE_MANYTREE_FN(szPath, RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK),
794 1, g_nsTestRun, "RTPathQueryInfoEx/manytree/NOTHING");
795 PROFILE_MANYTREE_FN(szPath, RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK),
796 1, g_nsTestRun, "RTPathQueryInfoEx/manytree/UNIX");
797}
798
799
800void fsPerfChmod(void)
801{
802 RTTestISub("chmod");
803
804 /* Non-existing files. */
805 RTTESTI_CHECK_RC(RTPathSetMode(InEmptyDir(RT_STR_TUPLE("no-such-file")), 0665),
806 VERR_FILE_NOT_FOUND);
807 RTTESTI_CHECK_RC(RTPathSetMode(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), 0665),
808 FSPERF_VERR_PATH_NOT_FOUND);
809 RTTESTI_CHECK_RC(RTPathSetMode(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), 0665), VERR_PATH_NOT_FOUND);
810
811 /* Shallow: */
812 RTFILE hFile1;
813 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file14")),
814 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
815 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
816
817 RTFSOBJINFO ObjInfo;
818 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
819 RTFMODE const fEvenMode = (ObjInfo.Attr.fMode & ~RTFS_UNIX_ALL_ACCESS_PERMS) | RTFS_DOS_READONLY | 0400;
820 RTFMODE const fOddMode = (ObjInfo.Attr.fMode & ~(RTFS_UNIX_ALL_ACCESS_PERMS | RTFS_DOS_READONLY)) | 0640;
821 PROFILE_FN(RTPathSetMode(g_szDir, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTPathSetMode");
822 RTPathSetMode(g_szDir, ObjInfo.Attr.fMode);
823
824 /* Deep: */
825 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file14")),
826 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
827 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
828
829 PROFILE_FN(RTPathSetMode(g_szDeepDir, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTPathSetMode/deep");
830 RTPathSetMode(g_szDeepDir, ObjInfo.Attr.fMode);
831
832 /* Manytree: */
833 char szPath[RTPATH_MAX];
834 PROFILE_MANYTREE_FN(szPath, RTPathSetMode(szPath, iIteration & 1 ? fOddMode : fEvenMode), 1, g_nsTestRun,
835 "RTPathSetMode/manytree");
836 DO_MANYTREE_FN(szPath, RTPathSetMode(szPath, ObjInfo.Attr.fMode));
837}
838
839
840void fsPerfUtimes(void)
841{
842 RTTestISub("utimes");
843
844 RTTIMESPEC Time1;
845 RTTimeNow(&Time1);
846 RTTIMESPEC Time2 = Time1;
847 RTTimeSpecSubSeconds(&Time2, 3636);
848
849 /* Non-existing files. */
850 RTTESTI_CHECK_RC(RTPathSetTimesEx(InEmptyDir(RT_STR_TUPLE("no-such-file")), NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
851 VERR_FILE_NOT_FOUND);
852 RTTESTI_CHECK_RC(RTPathSetTimesEx(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
853 NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
854 FSPERF_VERR_PATH_NOT_FOUND);
855 RTTESTI_CHECK_RC(RTPathSetTimesEx(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
856 NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
857 VERR_PATH_NOT_FOUND);
858
859 /* Shallow: */
860 RTFILE hFile1;
861 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file15")),
862 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
863 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
864
865 RTFSOBJINFO ObjInfo0 = {0};
866 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo0, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
867
868 /* Modify modification time: */
869 RTTESTI_CHECK_RC(RTPathSetTimesEx(g_szDir, NULL, &Time2, NULL, NULL, RTPATH_F_ON_LINK), VINF_SUCCESS);
870 RTFSOBJINFO ObjInfo1;
871 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo1, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
872 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo1.ModificationTime) >> 2) == (RTTimeSpecGetSeconds(&Time2) >> 2));
873 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo1.AccessTime) / 1000 == RTTimeSpecGetNano(&ObjInfo0.AccessTime) / 1000 /* posix timeval */);
874
875 /* Modify access time: */
876 RTTESTI_CHECK_RC(RTPathSetTimesEx(g_szDir, &Time1, NULL, NULL, NULL, RTPATH_F_ON_LINK), VINF_SUCCESS);
877 RTFSOBJINFO ObjInfo2 = {0};
878 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo2, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
879 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo2.AccessTime) >> 2) == (RTTimeSpecGetSeconds(&Time1) >> 2));
880 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo2.ModificationTime) / 1000 == RTTimeSpecGetNano(&ObjInfo1.ModificationTime) / 1000 /* posix timeval */);
881
882 /* Profile shallow: */
883 PROFILE_FN(RTPathSetTimesEx(g_szDir, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
884 NULL, NULL, RTPATH_F_ON_LINK),
885 g_nsTestRun, "RTPathSetTimesEx");
886
887 /* Deep: */
888 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file15")),
889 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
890 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
891
892 PROFILE_FN(RTPathSetTimesEx(g_szDeepDir, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
893 NULL, NULL, RTPATH_F_ON_LINK),
894 g_nsTestRun, "RTPathSetTimesEx/deep");
895
896 /* Manytree: */
897 char szPath[RTPATH_MAX];
898 PROFILE_MANYTREE_FN(szPath, RTPathSetTimesEx(szPath, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
899 NULL, NULL, RTPATH_F_ON_LINK),
900 1, g_nsTestRun, "RTPathSetTimesEx/manytree");
901}
902
903
904DECL_FORCE_INLINE(int) fsPerfRenameMany(const char *pszFile, uint32_t iIteration)
905{
906 char szRenamed[RTPATH_MAX];
907 strcat(strcpy(szRenamed, pszFile), "-renamed");
908 if (!(iIteration & 1))
909 return RTPathRename(pszFile, szRenamed, 0);
910 return RTPathRename(szRenamed, pszFile, 0);
911}
912
913
914void fsPerfRename(void)
915{
916 RTTestISub("rename");
917 char szPath[RTPATH_MAX];
918
919 /* Non-existing files. */
920 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("other-no-such-file")));
921 RTTESTI_CHECK_RC(RTPathRename(InEmptyDir(RT_STR_TUPLE("no-such-file")), szPath, 0), VERR_FILE_NOT_FOUND);
922 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "other-no-such-file")));
923 RTTESTI_CHECK_RC(RTPathRename(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), szPath, 0),
924 FSPERF_VERR_PATH_NOT_FOUND);
925 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("other-no-such-file")));
926 RTTESTI_CHECK_RC(RTPathRename(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), szPath, 0), VERR_PATH_NOT_FOUND);
927
928 RTFILE hFile1;
929 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file16")),
930 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
931 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
932 strcat(strcpy(szPath, g_szDir), "-no-such-dir" RTPATH_SLASH_STR "file16");
933 RTTESTI_CHECK_RC(RTPathRename(szPath, g_szDir, 0), FSPERF_VERR_PATH_NOT_FOUND);
934 RTTESTI_CHECK_RC(RTPathRename(g_szDir, szPath, 0), FSPERF_VERR_PATH_NOT_FOUND);
935
936 /* Shallow: */
937 strcat(strcpy(szPath, g_szDir), "-other");
938 PROFILE_FN(RTPathRename(iIteration & 1 ? szPath : g_szDir, iIteration & 1 ? g_szDir : szPath, 0), g_nsTestRun, "RTPathRename");
939
940 /* Deep: */
941 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file15")),
942 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
943 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
944
945 strcat(strcpy(szPath, g_szDeepDir), "-other");
946 PROFILE_FN(RTPathRename(iIteration & 1 ? szPath : g_szDeepDir, iIteration & 1 ? g_szDeepDir : szPath, 0),
947 g_nsTestRun, "RTPathRename/deep");
948
949 /* Manytree: */
950 PROFILE_MANYTREE_FN(szPath, fsPerfRenameMany(szPath, iIteration), 2, g_nsTestRun, "RTPathRename/manytree");
951}
952
953
954DECL_FORCE_INLINE(int) fsPerfEnumEmpty(void)
955{
956 RTDIR hDir;
957 g_szEmptyDir[g_cchEmptyDir] = '\0';
958 RTTESTI_CHECK_RC_RET(RTDirOpen(&hDir, g_szEmptyDir), VINF_SUCCESS, rcCheck);
959
960 RTDIRENTRY Entry;
961 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
962 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
963 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
964
965 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
966 return VINF_SUCCESS;
967}
968
969
970DECL_FORCE_INLINE(int) fsPerfEnumManyFiles(void)
971{
972 RTDIR hDir;
973 RTTESTI_CHECK_RC_RET(RTDirOpen(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS, rcCheck);
974 uint32_t cLeft = g_cManyFiles + 2;
975 for (;;)
976 {
977 RTDIRENTRY Entry;
978 if (cLeft > 0)
979 RTTESTI_CHECK_RC_BREAK(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
980 else
981 {
982 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
983 break;
984 }
985 cLeft--;
986 }
987 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
988 return VINF_SUCCESS;
989}
990
991
992void vsPerfDirEnum(void)
993{
994 RTTestISub("dir enum");
995 RTDIR hDir;
996
997 /* Non-existing files. */
998 RTTESTI_CHECK_RC(RTDirOpen(&hDir, InEmptyDir(RT_STR_TUPLE("no-such-file"))), VERR_FILE_NOT_FOUND);
999 RTTESTI_CHECK_RC(RTDirOpen(&hDir, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
1000 RTTESTI_CHECK_RC(RTDirOpen(&hDir, InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
1001
1002 /*
1003 * The empty directory.
1004 */
1005 g_szEmptyDir[g_cchEmptyDir] = '\0';
1006 RTTESTI_CHECK_RC_RETV(RTDirOpen(&hDir, g_szEmptyDir), VINF_SUCCESS);
1007
1008 uint32_t fDots = 0;
1009 RTDIRENTRY Entry;
1010 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
1011 RTTESTI_CHECK(RTDirEntryIsStdDotLink(&Entry));
1012 fDots |= RT_BIT_32(Entry.cbName - 1);
1013
1014 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
1015 RTTESTI_CHECK(RTDirEntryIsStdDotLink(&Entry));
1016 fDots |= RT_BIT_32(Entry.cbName - 1);
1017 RTTESTI_CHECK(fDots == 3);
1018
1019 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
1020
1021 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
1022
1023 /*
1024 * The directory with many files in it.
1025 */
1026 if (g_fManyFiles)
1027 {
1028 fDots = 0;
1029 uint32_t const cBitmap = RT_ALIGN_32(g_cManyFiles, 64);
1030 void *pvBitmap = alloca(cBitmap / 8);
1031 RT_BZERO(pvBitmap, cBitmap / 8);
1032 for (uint32_t i = g_cManyFiles; i < cBitmap; i++)
1033 ASMBitSet(pvBitmap, i);
1034
1035 uint32_t cFiles = 0;
1036 RTTESTI_CHECK_RC_RETV(RTDirOpen(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS);
1037 for (;;)
1038 {
1039 int rc = RTDirRead(hDir, &Entry, NULL);
1040 if (rc == VINF_SUCCESS)
1041 {
1042 if (Entry.szName[0] == '.')
1043 {
1044 if (Entry.szName[1] == '.')
1045 {
1046 RTTESTI_CHECK(!(fDots & 2));
1047 fDots |= 2;
1048 }
1049 else
1050 {
1051 RTTESTI_CHECK(Entry.szName[1] == '\0');
1052 RTTESTI_CHECK(!(fDots & 1));
1053 fDots |= 1;
1054 }
1055 }
1056 else
1057 {
1058 uint32_t iFile = UINT32_MAX;
1059 RTTESTI_CHECK_RC(RTStrToUInt32Full(Entry.szName, 10, &iFile), VINF_SUCCESS);
1060 if ( iFile < g_cManyFiles
1061 && !ASMBitTest(pvBitmap, iFile))
1062 {
1063 ASMBitSet(pvBitmap, iFile);
1064 cFiles++;
1065 }
1066 else
1067 RTTestFailed(g_hTest, "line %u: iFile=%u g_cManyFiles=%u\n", __LINE__, iFile, g_cManyFiles);
1068 }
1069 }
1070 else if (rc == VERR_NO_MORE_FILES)
1071 break;
1072 else
1073 {
1074 RTTestFailed(g_hTest, "RTDirRead failed enumerating manyfiles: %Rrc\n", rc);
1075 RTDirClose(hDir);
1076 return;
1077 }
1078 }
1079 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
1080 RTTESTI_CHECK(fDots == 3);
1081 RTTESTI_CHECK(cFiles == g_cManyFiles);
1082 RTTESTI_CHECK(ASMMemIsAllU8(pvBitmap, cBitmap / 8, 0xff));
1083 }
1084
1085 /*
1086 * Profile.
1087 */
1088 PROFILE_FN(fsPerfEnumEmpty(),g_nsTestRun, "RTDirOpen/Read/Close empty");
1089 if (g_fManyFiles)
1090 PROFILE_FN(fsPerfEnumManyFiles(), g_nsTestRun, "RTDirOpen/Read/Close manyfiles");
1091}
1092
1093
1094void fsPerfMkRmDir(void)
1095{
1096 RTTestISub("mkdir/rmdir");
1097
1098 /* Non-existing directories: */
1099 RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir"))), VERR_FILE_NOT_FOUND);
1100 RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
1101 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
1102 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), 0755, 0), FSPERF_VERR_PATH_NOT_FOUND);
1103 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), 0755, 0), VERR_PATH_NOT_FOUND);
1104
1105 /** @todo check what happens if non-final path component isn't a directory. unix
1106 * should return ENOTDIR and IPRT translates that to VERR_PATH_NOT_FOUND.
1107 * Curious what happens on windows. */
1108
1109 /* Already existing directories and files: */
1110 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE(".")), 0755, 0), VERR_ALREADY_EXISTS);
1111 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE("..")), 0755, 0), VERR_ALREADY_EXISTS);
1112
1113 /* Remove directory with subdirectories: */
1114#if defined(RT_OS_WINDOWS)
1115 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("."))), VERR_DIR_NOT_EMPTY);
1116#else
1117 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("."))), VERR_INVALID_PARAMETER); /* EINVAL for '.' */
1118#endif
1119#if defined(RT_OS_WINDOWS)
1120 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(".."))), VERR_SHARING_VIOLATION); /* weird */
1121#else
1122 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(".."))), VERR_DIR_NOT_EMPTY);
1123#endif
1124 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(""))), VERR_DIR_NOT_EMPTY);
1125
1126 /* Create a directory and remove it: */
1127 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("subdir-1")), 0755, 0), VINF_SUCCESS);
1128 RTTESTI_CHECK_RC(RTDirRemove(g_szDir), VINF_SUCCESS);
1129
1130 /* Create a file and try remove it or create a directory with the same name: */
1131 RTFILE hFile1;
1132 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file18")),
1133 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
1134 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
1135 RTTESTI_CHECK_RC(RTDirRemove(g_szDir), VERR_NOT_A_DIRECTORY);
1136 RTTESTI_CHECK_RC(RTDirCreate(g_szDir, 0755, 0), VERR_ALREADY_EXISTS);
1137 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("file18" RTPATH_SLASH_STR "subdir")), 0755, 0), VERR_PATH_NOT_FOUND);
1138
1139 /*
1140 * Profile alternately creating and removing a bunch of directories.
1141 */
1142 RTTESTI_CHECK_RC_RETV(RTDirCreate(InDir(RT_STR_TUPLE("subdir-2")), 0755, 0), VINF_SUCCESS);
1143 size_t cchDir = strlen(g_szDir);
1144 g_szDir[cchDir++] = RTPATH_SLASH;
1145 g_szDir[cchDir++] = 's';
1146
1147 uint32_t cCreated = 0;
1148 uint64_t nsCreate = 0;
1149 uint64_t nsRemove = 0;
1150 for (;;)
1151 {
1152 /* Create a bunch: */
1153 uint64_t nsStart = RTTimeNanoTS();
1154 for (uint32_t i = 0; i < 998; i++)
1155 {
1156 RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD);
1157 RTTESTI_CHECK_RC_RETV(RTDirCreate(g_szDir, 0755, 0), VINF_SUCCESS);
1158 }
1159 nsCreate += RTTimeNanoTS() - nsStart;
1160 cCreated += 998;
1161
1162 /* Remove the bunch: */
1163 nsStart = RTTimeNanoTS();
1164 for (uint32_t i = 0; i < 998; i++)
1165 {
1166 RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD);
1167 RTTESTI_CHECK_RC_RETV(RTDirRemove(g_szDir), VINF_SUCCESS);
1168 }
1169 nsRemove = RTTimeNanoTS() - nsStart;
1170
1171 /* Check if we got time for another round: */
1172 if ( ( nsRemove >= g_nsTestRun
1173 && nsCreate >= g_nsTestRun)
1174 || nsCreate + nsRemove >= g_nsTestRun * 3)
1175 break;
1176 }
1177 RTTestIValue("RTDirCreate", nsCreate / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE);
1178 RTTestIValue("RTDirRemove", nsRemove / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE);
1179}
1180
1181
1182void fsPerfStatVfs(void)
1183{
1184 RTTestISub("statvfs");
1185
1186 g_szEmptyDir[g_cchEmptyDir] = '\0';
1187 RTFOFF cbTotal;
1188 RTFOFF cbFree;
1189 uint32_t cbBlock;
1190 uint32_t cbSector;
1191 RTTESTI_CHECK_RC(RTFsQuerySizes(g_szEmptyDir, &cbTotal, &cbFree, &cbBlock, &cbSector), VINF_SUCCESS);
1192
1193 uint32_t uSerial;
1194 RTTESTI_CHECK_RC(RTFsQuerySerial(g_szEmptyDir, &uSerial), VINF_SUCCESS);
1195
1196 RTFSPROPERTIES Props;
1197 RTTESTI_CHECK_RC(RTFsQueryProperties(g_szEmptyDir, &Props), VINF_SUCCESS);
1198
1199 RTFSTYPE enmType;
1200 RTTESTI_CHECK_RC(RTFsQueryType(g_szEmptyDir, &enmType), VINF_SUCCESS);
1201
1202}
1203
1204
1205void fsPerfRm(void)
1206{
1207 RTTestISub("rm");
1208
1209 /* Non-existing files. */
1210 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-file"))), VERR_FILE_NOT_FOUND);
1211 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
1212 RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
1213
1214 /* Directories: */
1215#if defined(RT_OS_WINDOWS)
1216 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("."))), VERR_ACCESS_DENIED);
1217 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(".."))), VERR_ACCESS_DENIED);
1218 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(""))), VERR_ACCESS_DENIED);
1219#else
1220 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("."))), VERR_IS_A_DIRECTORY);
1221 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(".."))), VERR_IS_A_DIRECTORY);
1222 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(""))), VERR_IS_A_DIRECTORY);
1223#endif
1224
1225 /* Shallow: */
1226 RTFILE hFile1;
1227 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file19")),
1228 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
1229 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
1230 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
1231 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VERR_FILE_NOT_FOUND);
1232
1233 if (g_fManyFiles)
1234 {
1235 /*
1236 * Profile the deletion of the manyfiles content.
1237 */
1238 {
1239 InDir(RT_STR_TUPLE("manyfiles" RTPATH_SLASH_STR));
1240 size_t const offFilename = strlen(g_szDir);
1241 fsPerfYield();
1242 uint64_t const nsStart = RTTimeNanoTS();
1243 for (uint32_t i = 0; i < g_cManyFiles; i++)
1244 {
1245 RTStrFormatU32(&g_szDir[offFilename], sizeof(g_szDir) - offFilename, i, 10, 5, 5, RTSTR_F_ZEROPAD);
1246 RTTESTI_CHECK_RC_RETV(RTFileDelete(g_szDir), VINF_SUCCESS);
1247 }
1248 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
1249 RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Deleted %u empty files from a single directory", g_cManyFiles);
1250 RTTestIValueF(cNsElapsed / g_cManyFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Delete file (single dir)");
1251 }
1252
1253 /*
1254 * Ditto for the manytree.
1255 */
1256 {
1257 char szPath[RTPATH_MAX];
1258 uint64_t const nsStart = RTTimeNanoTS();
1259 DO_MANYTREE_FN(szPath, RTTESTI_CHECK_RC_RETV(RTFileDelete(szPath), VINF_SUCCESS));
1260 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
1261 RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Deleted %u empty files in tree", g_cManyTreeFiles);
1262 RTTestIValueF(cNsElapsed / g_cManyTreeFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Delete file (tree)");
1263 }
1264 }
1265}
1266
1267
1268void fsPerfChSize(void)
1269{
1270 RTTestISub("chsize");
1271
1272 /*
1273 * We need some free space to perform this test.
1274 */
1275 g_szDir[g_cchDir] = '\0';
1276 RTFOFF cbFree = 0;
1277 RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
1278 if (cbFree < _1M)
1279 {
1280 RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 1MB", cbFree);
1281 return;
1282 }
1283
1284 /*
1285 * Create a file and play around with it's size.
1286 * We let the current file position follow the end position as we make changes.
1287 */
1288 RTFILE hFile1;
1289 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file20")),
1290 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
1291 uint64_t cbFile = UINT64_MAX;
1292 RTTESTI_CHECK_RC(RTFileGetSize(hFile1, &cbFile), VINF_SUCCESS);
1293 RTTESTI_CHECK(cbFile == 0);
1294
1295 uint8_t abBuf[4096];
1296 static uint64_t const s_acbChanges[] =
1297 {
1298 1023, 1024, 1024, 1025, 8192, 11111, _1M, _8M, _8M,
1299 _4M, _2M + 1, _1M - 1, 65537, 65536, 32768, 8000, 7999, 7998, 1024, 1, 0
1300 };
1301 uint64_t cbOld = 0;
1302 for (unsigned i = 0; i < RT_ELEMENTS(s_acbChanges); i++)
1303 {
1304 uint64_t cbNew = s_acbChanges[i];
1305 if (cbNew + _64K >= (uint64_t)cbFree)
1306 continue;
1307
1308 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, cbNew), VINF_SUCCESS);
1309 RTTESTI_CHECK_RC(RTFileGetSize(hFile1, &cbFile), VINF_SUCCESS);
1310 RTTESTI_CHECK_MSG(cbFile == cbNew, ("cbFile=%#RX64 cbNew=%#RX64\n", cbFile, cbNew));
1311
1312 if (cbNew > cbOld)
1313 {
1314 /* Check that the extension is all zeroed: */
1315 uint64_t cbLeft = cbNew - cbOld;
1316 while (cbLeft > 0)
1317 {
1318 memset(abBuf, 0xff, sizeof(abBuf));
1319 size_t cbToRead = sizeof(abBuf);
1320 if (cbToRead > cbLeft)
1321 cbToRead = (size_t)cbLeft;
1322 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, cbToRead, NULL), VINF_SUCCESS);
1323 RTTESTI_CHECK(ASMMemIsZero(abBuf, cbToRead));
1324 cbLeft -= cbToRead;
1325 }
1326 }
1327 else
1328 {
1329 /* Check that reading fails with EOF because current position is now beyond the end: */
1330 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 1, NULL), VERR_EOF);
1331
1332 /* Keep current position at the end of the file: */
1333 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbNew, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1334 }
1335 cbOld = cbNew;
1336 }
1337
1338 /*
1339 * Profile just the file setting operation itself, keeping the changes within
1340 * an allocation unit to avoid needing to adjust the actual (host) FS allocation.
1341 * ASSUMES allocation unit >= 512 and power of two.
1342 */
1343 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, _64K), VINF_SUCCESS);
1344 PROFILE_FN(RTFileSetSize(hFile1, _64K - (iIteration & 255) - 128), g_nsTestRun, "RTFileSetSize/noalloc");
1345
1346 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS);
1347 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
1348 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
1349}
1350
1351
1352int fsPerfIoPrepFile(RTFILE hFile1, uint64_t cbFile, uint8_t **ppbFree)
1353{
1354 /*
1355 * Seek to the end - 4K and write the last 4K.
1356 * This should have the effect of filling the whole file with zeros.
1357 */
1358 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, cbFile - _4K, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1359 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, g_abRTZero4K, _4K, NULL), VINF_SUCCESS, rcCheck);
1360
1361 /*
1362 * Check that the space we searched across actually is zero filled.
1363 */
1364 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1365 size_t cbBuf = _1M;
1366 uint8_t *pbBuf = *ppbFree = (uint8_t *)RTMemAlloc(cbBuf);
1367 RTTESTI_CHECK_RET(pbBuf != NULL, VERR_NO_MEMORY);
1368 uint64_t cbLeft = cbFile;
1369 while (cbLeft > 0)
1370 {
1371 size_t cbToRead = cbBuf;
1372 if (cbToRead > cbLeft)
1373 cbToRead = (size_t)cbLeft;
1374 pbBuf[cbToRead] = 0xff;
1375
1376 RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBuf, cbToRead, NULL), VINF_SUCCESS, rcCheck);
1377 RTTESTI_CHECK_RET(ASMMemIsZero(pbBuf, cbToRead), VERR_MISMATCH);
1378
1379 cbLeft -= cbToRead;
1380 }
1381
1382 /*
1383 * Fill the file with 0xf6 and insert offset markers with 1KB intervals.
1384 */
1385 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1386 memset(pbBuf, 0xf6, cbBuf);
1387 cbLeft = cbFile;
1388 uint64_t off = 0;
1389 while (cbLeft > 0)
1390 {
1391 Assert(!(off & (_1K - 1)));
1392 Assert(!(cbBuf & (_1K - 1)));
1393 for (size_t offBuf = 0; offBuf < cbBuf; offBuf += _1K, off += _1K)
1394 *(uint64_t *)&pbBuf[offBuf] = off;
1395
1396 size_t cbToWrite = cbBuf;
1397 if (cbToWrite > cbLeft)
1398 cbToWrite = (size_t)cbLeft;
1399
1400 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBuf, cbToWrite, NULL), VINF_SUCCESS, rcCheck);
1401
1402 cbLeft -= cbToWrite;
1403 }
1404
1405 return VINF_SUCCESS;
1406}
1407
1408
1409/**
1410 * Checks the content read from the file fsPerfIoPrepFile() prepared.
1411 */
1412bool fsPerfCheckReadBuf(unsigned uLineNo, uint64_t off, uint8_t const *pbBuf, size_t cbBuf, uint8_t bFiller = 0xf6)
1413{
1414 uint32_t cMismatches = 0;
1415 size_t offBuf = 0;
1416 uint32_t offBlock = (uint32_t)(off & (_1K - 1));
1417 while (offBuf < cbBuf)
1418 {
1419 /*
1420 * Check the offset marker:
1421 */
1422 if (offBlock < sizeof(uint64_t))
1423 {
1424 RTUINT64U uMarker;
1425 uMarker.u = off + offBuf - offBlock;
1426 unsigned offMarker = offBlock & (sizeof(uint64_t) - 1);
1427 while (offMarker < sizeof(uint64_t) && offBuf < cbBuf)
1428 {
1429 if (uMarker.au8[offMarker] != pbBuf[offBuf])
1430 {
1431 RTTestIFailed("%u: Mismatch at buffer/file offset %#zx/%#RX64: %#x, expected %#x",
1432 uLineNo, offBuf, off + offBuf, pbBuf[offBuf], uMarker.au8[offMarker]);
1433 if (cMismatches++ > 32)
1434 return false;
1435 }
1436 offMarker++;
1437 offBuf++;
1438 }
1439 offBlock = sizeof(uint64_t);
1440 }
1441
1442 /*
1443 * Check the filling:
1444 */
1445 size_t cbFilling = RT_MIN(_1K - offBlock, cbBuf - offBuf);
1446 if ( cbFilling == 0
1447 || ASMMemIsAllU8(&pbBuf[offBuf], cbFilling, bFiller))
1448 offBuf += cbFilling;
1449 else
1450 {
1451 /* Some mismatch, locate it/them: */
1452 while (cbFilling > 0 && offBuf < cbBuf)
1453 {
1454 if (pbBuf[offBuf] != bFiller)
1455 {
1456 RTTestIFailed("%u: Mismatch at buffer/file offset %#zx/%#RX64: %#x, expected %#04x",
1457 uLineNo, offBuf, off + offBuf, pbBuf[offBuf], bFiller);
1458 if (cMismatches++ > 32)
1459 return false;
1460 }
1461 offBuf++;
1462 cbFilling--;
1463 }
1464 }
1465 offBlock = 0;
1466 }
1467 return cMismatches == 0;
1468}
1469
1470
1471/**
1472 * Sets up write buffer with offset markers and fillers.
1473 */
1474void fsPerfFillWriteBuf(uint64_t off, uint8_t *pbBuf, size_t cbBuf, uint8_t bFiller = 0xf6)
1475{
1476 uint32_t offBlock = (uint32_t)(off & (_1K - 1));
1477 while (cbBuf > 0)
1478 {
1479 /* The marker. */
1480 if (offBlock < sizeof(uint64_t))
1481 {
1482 RTUINT64U uMarker;
1483 uMarker.u = off + offBlock;
1484 if (cbBuf > sizeof(uMarker) - offBlock)
1485 {
1486 memcpy(pbBuf, &uMarker.au8[offBlock], sizeof(uMarker) - offBlock);
1487 pbBuf += sizeof(uMarker) - offBlock;
1488 cbBuf -= sizeof(uMarker) - offBlock;
1489 off += sizeof(uMarker) - offBlock;
1490 }
1491 else
1492 {
1493 memcpy(pbBuf, &uMarker.au8[offBlock], cbBuf);
1494 return;
1495 }
1496 offBlock = sizeof(uint64_t);
1497 }
1498
1499 /* Do the filling. */
1500 size_t cbFilling = RT_MIN(_1K - offBlock, cbBuf);
1501 memset(pbBuf, bFiller, cbFilling);
1502 pbBuf += cbFilling;
1503 cbBuf -= cbFilling;
1504 off += cbFilling;
1505
1506 offBlock = 0;
1507 }
1508}
1509
1510
1511
1512void fsPerfIoSeek(RTFILE hFile1, uint64_t cbFile)
1513{
1514 /*
1515 * Do a bunch of search tests, most which are random.
1516 */
1517 struct
1518 {
1519 int rc;
1520 uint32_t uMethod;
1521 int64_t offSeek;
1522 uint64_t offActual;
1523
1524 } aSeeks[9 + 64] =
1525 {
1526 { VINF_SUCCESS, RTFILE_SEEK_BEGIN, 0, 0 },
1527 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 },
1528 { VINF_SUCCESS, RTFILE_SEEK_END, 0, cbFile },
1529 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -4096, cbFile - 4096 },
1530 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 4096 - (int64_t)cbFile, 0 },
1531 { VINF_SUCCESS, RTFILE_SEEK_END, -(int64_t)cbFile/2, cbFile / 2 + (cbFile & 1) },
1532 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -(int64_t)cbFile/2, 0 },
1533#if defined(RT_OS_WINDOWS)
1534 { VERR_NEGATIVE_SEEK, RTFILE_SEEK_CURRENT, -1, 0 },
1535#else
1536 { VERR_INVALID_PARAMETER, RTFILE_SEEK_CURRENT, -1, 0 },
1537#endif
1538 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 },
1539 };
1540
1541 uint64_t offActual = 0;
1542 for (unsigned i = 9; i < RT_ELEMENTS(aSeeks); i++)
1543 {
1544 switch (RTRandU32Ex(RTFILE_SEEK_BEGIN, RTFILE_SEEK_END))
1545 {
1546 default: AssertFailedBreak();
1547 case RTFILE_SEEK_BEGIN:
1548 aSeeks[i].uMethod = RTFILE_SEEK_BEGIN;
1549 aSeeks[i].rc = VINF_SUCCESS;
1550 aSeeks[i].offSeek = RTRandU64Ex(0, cbFile + cbFile / 8);
1551 aSeeks[i].offActual = offActual = aSeeks[i].offSeek;
1552 break;
1553
1554 case RTFILE_SEEK_CURRENT:
1555 aSeeks[i].uMethod = RTFILE_SEEK_CURRENT;
1556 aSeeks[i].rc = VINF_SUCCESS;
1557 aSeeks[i].offSeek = (int64_t)RTRandU64Ex(0, cbFile + cbFile / 8) - (int64_t)offActual;
1558 aSeeks[i].offActual = offActual += aSeeks[i].offSeek;
1559 break;
1560
1561 case RTFILE_SEEK_END:
1562 aSeeks[i].uMethod = RTFILE_SEEK_END;
1563 aSeeks[i].rc = VINF_SUCCESS;
1564 aSeeks[i].offSeek = -(int64_t)RTRandU64Ex(0, cbFile);
1565 aSeeks[i].offActual = offActual = cbFile + aSeeks[i].offSeek;
1566 break;
1567 }
1568 }
1569
1570 for (unsigned iDoReadCheck = 0; iDoReadCheck < 2; iDoReadCheck++)
1571 {
1572 for (uint32_t i = 0; i < RT_ELEMENTS(aSeeks); i++)
1573 {
1574 offActual = UINT64_MAX;
1575 int rc = RTFileSeek(hFile1, aSeeks[i].offSeek, aSeeks[i].uMethod, &offActual);
1576 if (rc != aSeeks[i].rc)
1577 RTTestIFailed("Seek #%u: Expected %Rrc, got %Rrc", i, aSeeks[i].rc, rc);
1578 if (RT_SUCCESS(rc) && offActual != aSeeks[i].offActual)
1579 RTTestIFailed("Seek #%u: offActual %#RX64, expected %#RX64", i, offActual, aSeeks[i].offActual);
1580 if (RT_SUCCESS(rc))
1581 {
1582 uint64_t offTell = RTFileTell(hFile1);
1583 if (offTell != offActual)
1584 RTTestIFailed("Seek #%u: offActual %#RX64, RTFileTell %#RX64", i, offActual, offTell);
1585 }
1586
1587 if (RT_SUCCESS(rc) && offActual + _2K <= cbFile && iDoReadCheck)
1588 {
1589 uint8_t abBuf[_2K];
1590 RTTESTI_CHECK_RC(rc = RTFileRead(hFile1, abBuf, sizeof(abBuf), NULL), VINF_SUCCESS);
1591 if (RT_SUCCESS(rc))
1592 {
1593 size_t offMarker = (size_t)(RT_ALIGN_64(offActual, _1K) - offActual);
1594 uint64_t uMarker = *(uint64_t *)&abBuf[offMarker]; /** @todo potentially unaligned access */
1595 if (uMarker != offActual + offMarker)
1596 RTTestIFailed("Seek #%u: Invalid marker value (@ %#RX64): %#RX64, expected %#RX64",
1597 i, offActual, uMarker, offActual + offMarker);
1598
1599 RTTESTI_CHECK_RC(RTFileSeek(hFile1, -(int64_t)sizeof(abBuf), RTFILE_SEEK_CURRENT, NULL), VINF_SUCCESS);
1600 }
1601 }
1602 }
1603 }
1604
1605
1606 /*
1607 * Profile seeking relative to the beginning of the file and relative
1608 * to the end. The latter might be more expensive in a SF context.
1609 */
1610 PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? iIteration : iIteration % cbFile, RTFILE_SEEK_BEGIN, NULL),
1611 g_nsTestRun, "RTFileSeek/BEGIN");
1612 PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? -(int64_t)iIteration : -(int64_t)(iIteration % cbFile), RTFILE_SEEK_END, NULL),
1613 g_nsTestRun, "RTFileSeek/END");
1614
1615}
1616
1617
1618/** For fsPerfIoRead and fsPerfIoWrite. */
1619#define PROFILE_IO_FN(a_szOperation, a_fnCall) \
1620 do \
1621 { \
1622 RTTESTI_CHECK_RC_RETV(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS); \
1623 uint64_t offActual = 0; \
1624 uint32_t cSeeks = 0; \
1625 \
1626 /* Estimate how many iterations we need to fill up the given timeslot: */ \
1627 fsPerfYield(); \
1628 uint64_t nsStart = RTTimeNanoTS(); \
1629 uint64_t ns; \
1630 do \
1631 ns = RTTimeNanoTS(); \
1632 while (ns == nsStart); \
1633 nsStart = ns; \
1634 \
1635 uint64_t iIteration = 0; \
1636 do \
1637 { \
1638 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
1639 iIteration++; \
1640 ns = RTTimeNanoTS() - nsStart; \
1641 } while (ns < RT_NS_10MS); \
1642 ns /= iIteration; \
1643 if (ns > g_nsPerNanoTSCall + 32) \
1644 ns -= g_nsPerNanoTSCall; \
1645 uint64_t cIterations = g_nsTestRun / ns; \
1646 \
1647 /* Do the actual profiling: */ \
1648 cSeeks = 0; \
1649 iIteration = 0; \
1650 fsPerfYield(); \
1651 nsStart = RTTimeNanoTS(); \
1652 for (uint32_t iAdjust = 0; iAdjust < 4; iAdjust++) \
1653 { \
1654 for (; iIteration < cIterations; iIteration++)\
1655 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
1656 ns = RTTimeNanoTS() - nsStart;\
1657 if (ns >= g_nsTestRun - (g_nsTestRun / 10)) \
1658 break; \
1659 cIterations += cIterations / 4; \
1660 if (cIterations & 1) \
1661 cIterations++; \
1662 nsStart += g_nsPerNanoTSCall; \
1663 } \
1664 RTTestIValueF(ns / iIteration, \
1665 RTTESTUNIT_NS_PER_OCCURRENCE, a_szOperation "/seq/%RU32 latency", cbBlock); \
1666 RTTestIValueF((uint64_t)iIteration * cbBlock / ((double)ns / RT_NS_1SEC), \
1667 RTTESTUNIT_BYTES_PER_SEC, a_szOperation "/seq/%RU32 throughput", cbBlock); \
1668 RTTestIValueF(iIteration, \
1669 RTTESTUNIT_CALLS, a_szOperation "/seq/%RU32 calls", cbBlock); \
1670 RTTestIValueF((uint64_t)iIteration * cbBlock, \
1671 RTTESTUNIT_BYTES, a_szOperation "/seq/%RU32 bytes", cbBlock); \
1672 RTTestIValueF(cSeeks, \
1673 RTTESTUNIT_OCCURRENCES, a_szOperation "/seq/%RU32 seeks", cbBlock); \
1674 if (g_fShowDuration) \
1675 RTTestIValueF(ns, RTTESTUNIT_NS, a_szOperation "/seq/%RU32 duration", cbBlock); \
1676 } while (0)
1677
1678
1679/**
1680 * One RTFileRead profiling iteration.
1681 */
1682DECL_FORCE_INLINE(int) fsPerfIoReadWorker(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t *pbBlock,
1683 uint64_t *poffActual, uint32_t *pcSeeks)
1684{
1685 /* Do we need to seek back to the start? */
1686 if (*poffActual + cbBlock <= cbFile)
1687 { /* likely */ }
1688 else
1689 {
1690 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1691 *pcSeeks += 1;
1692 *poffActual = 0;
1693 }
1694
1695 size_t cbActuallyRead = 0;
1696 RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBlock, cbBlock, &cbActuallyRead), VINF_SUCCESS, rcCheck);
1697 if (cbActuallyRead == cbBlock)
1698 {
1699 *poffActual += cbActuallyRead;
1700 return VINF_SUCCESS;
1701 }
1702 RTTestIFailed("RTFileRead at %#RX64 returned just %#x bytes, expected %#x", *poffActual, cbActuallyRead, cbBlock);
1703 *poffActual += cbActuallyRead;
1704 return VERR_READ_ERROR;
1705}
1706
1707
1708void fsPerfIoReadBlockSize(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock)
1709{
1710 RTTestISubF("IO - Sequential read %RU32", cbBlock);
1711
1712 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBlock);
1713 if (pbBuf)
1714 {
1715 memset(pbBuf, 0xf7, cbBlock);
1716 PROFILE_IO_FN("RTFileRead", fsPerfIoReadWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks));
1717 RTMemPageFree(pbBuf, cbBlock);
1718 }
1719 else
1720 RTTestSkipped(g_hTest, "insufficient (virtual) memory available");
1721}
1722
1723
1724void fsPerfRead(RTFILE hFile1, RTFILE hFileNoCache, uint64_t cbFile)
1725{
1726 RTTestISubF("IO - RTFileRead");
1727
1728 /*
1729 * Allocate a big buffer we can play around with. Min size is 1MB.
1730 */
1731 size_t cbBuf = cbFile < _64M ? (size_t)cbFile : _64M;
1732 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
1733 while (!pbBuf)
1734 {
1735 cbBuf /= 2;
1736 RTTESTI_CHECK_RETV(cbBuf >= _1M);
1737 pbBuf = (uint8_t *)RTMemPageAlloc(_32M);
1738 }
1739
1740#if 1
1741 /*
1742 * Start at the beginning and read the full buffer in random small chunks, thereby
1743 * checking that unaligned buffer addresses, size and file offsets work fine.
1744 */
1745 struct
1746 {
1747 uint64_t offFile;
1748 uint32_t cbMax;
1749 } aRuns[] = { { 0, 127 }, { cbFile - cbBuf, UINT32_MAX }, { 0, UINT32_MAX -1 }};
1750 for (uint32_t i = 0; i < RT_ELEMENTS(aRuns); i++)
1751 {
1752 memset(pbBuf, 0x55, cbBuf);
1753 RTTESTI_CHECK_RC(RTFileSeek(hFile1, aRuns[i].offFile, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1754 for (size_t offBuf = 0; offBuf < cbBuf; )
1755 {
1756 uint32_t const cbLeft = (uint32_t)(cbBuf - offBuf);
1757 uint32_t const cbToRead = aRuns[i].cbMax < UINT32_MAX / 2 ? RTRandU32Ex(1, RT_MIN(aRuns[i].cbMax, cbLeft))
1758 : aRuns[i].cbMax == UINT32_MAX ? RTRandU32Ex(RT_MAX(cbLeft / 4, 1), cbLeft)
1759 : RTRandU32Ex(cbLeft >= _8K ? _8K : 1, RT_MIN(_1M, cbLeft));
1760 size_t cbActual = 0;
1761 RTTESTI_CHECK_RC(RTFileRead(hFile1, &pbBuf[offBuf], cbToRead, &cbActual), VINF_SUCCESS);
1762 if (cbActual == cbToRead)
1763 offBuf += cbActual;
1764 else
1765 {
1766 RTTestIFailed("Attempting to read %#x bytes at %#zx, only got %#x bytes back! (cbLeft=%#x cbBuf=%#zx)\n",
1767 cbToRead, offBuf, cbActual, cbLeft, cbBuf);
1768 if (cbActual)
1769 offBuf += cbActual;
1770 else
1771 pbBuf[offBuf++] = 0x11;
1772 }
1773 }
1774 fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf);
1775 }
1776#endif
1777
1778 /*
1779 * Test reading beyond the end of the file.
1780 */
1781 size_t const acbMax[] = { cbBuf, _64K, _16K, _4K, 256 };
1782 uint32_t const aoffFromEos[] =
1783 { 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,
1784 4092, 4093, 4094, 4095, 4096, 4097, 4098, 4099, 4100, 8192, 16384, 32767, 32768, 32769, 65535, 65536, _1M - 1
1785 };
1786 for (unsigned iMax = 0; iMax < RT_ELEMENTS(acbMax); iMax++)
1787 {
1788 size_t const cbMaxRead = acbMax[iMax];
1789 for (uint32_t iOffFromEos = 0; iOffFromEos < RT_ELEMENTS(aoffFromEos); iOffFromEos++)
1790 {
1791 uint32_t off = aoffFromEos[iOffFromEos];
1792 if (off >= cbMaxRead)
1793 continue;
1794 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile - off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1795 size_t cbActual = ~(size_t)0;
1796 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, &cbActual), VINF_SUCCESS);
1797 RTTESTI_CHECK(cbActual == off);
1798
1799 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile - off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1800 cbActual = ~(size_t)0;
1801 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, off, &cbActual), VINF_SUCCESS);
1802 RTTESTI_CHECK_MSG(cbActual == off, ("%#zx vs %#zx", cbActual, off));
1803
1804 cbActual = ~(size_t)0;
1805 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, 1, &cbActual), VINF_SUCCESS);
1806 RTTESTI_CHECK(cbActual == 0);
1807
1808 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, NULL), VERR_EOF);
1809 }
1810 }
1811
1812 /*
1813 * Test reading beyond end of the file.
1814 */
1815 for (unsigned iMax = 0; iMax < RT_ELEMENTS(acbMax); iMax++)
1816 {
1817 size_t const cbMaxRead = acbMax[iMax];
1818 for (uint32_t off = 0; off < 256; off++)
1819 {
1820 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile + off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1821 size_t cbActual = ~(size_t)0;
1822 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, &cbActual), VINF_SUCCESS);
1823 RTTESTI_CHECK(cbActual == 0);
1824
1825 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, NULL), VERR_EOF);
1826 }
1827 }
1828
1829 /*
1830 * Do uncached access, must be page aligned.
1831 */
1832 uint32_t cbPage = PAGE_SIZE;
1833 memset(pbBuf, 0x66, cbBuf);
1834 RTTESTI_CHECK_RC(RTFileSeek(hFileNoCache, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1835 for (size_t offBuf = 0; offBuf < cbBuf; )
1836 {
1837 uint32_t const cPagesLeft = (uint32_t)((cbBuf - offBuf) / cbPage);
1838 uint32_t const cPagesToRead = RTRandU32Ex(1, cPagesLeft);
1839 size_t const cbToRead = cPagesToRead * (size_t)cbPage;
1840 size_t cbActual = 0;
1841 RTTESTI_CHECK_RC(RTFileRead(hFileNoCache, &pbBuf[offBuf], cbToRead, &cbActual), VINF_SUCCESS);
1842 if (cbActual == cbToRead)
1843 offBuf += cbActual;
1844 else
1845 {
1846 RTTestIFailed("Attempting to read %#zx bytes at %#zx, only got %#x bytes back!\n", cbToRead, offBuf, cbActual);
1847 if (cbActual)
1848 offBuf += cbActual;
1849 else
1850 {
1851 memset(&pbBuf[offBuf], 0x11, cbPage);
1852 offBuf += cbPage;
1853 }
1854 }
1855 }
1856 fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf);
1857
1858 /*
1859 * Check reading zero bytes at the end of the file.
1860 * Requires native call because RTFileWrite doesn't call kernel on zero byte reads.
1861 */
1862 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
1863#ifdef RT_OS_WINDOWS
1864 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
1865 NTSTATUS rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 0, NULL, NULL);
1866 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
1867 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
1868 RTTESTI_CHECK(Ios.Information == 0);
1869
1870 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1871 rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 1, NULL, NULL);
1872 RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE, ("rcNt=%#x", rcNt));
1873 RTTESTI_CHECK(Ios.Status == STATUS_END_OF_FILE);
1874 RTTESTI_CHECK(Ios.Information == 0);
1875#endif
1876
1877 /*
1878 * Other OS specific stuff.
1879 */
1880#ifdef RT_OS_WINDOWS
1881 /* Check that reading at an offset modifies the position: */
1882 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
1883 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile);
1884
1885 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
1886 LARGE_INTEGER offNt;
1887 offNt.QuadPart = cbFile / 2;
1888 rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, _4K, &offNt, NULL);
1889 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
1890 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
1891 RTTESTI_CHECK(Ios.Information == _4K);
1892 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile / 2 + _4K);
1893 fsPerfCheckReadBuf(__LINE__, cbFile / 2, pbBuf, _4K);
1894#endif
1895
1896 RTMemPageFree(pbBuf, cbBuf);
1897}
1898
1899
1900/**
1901 * One RTFileWrite profiling iteration.
1902 */
1903DECL_FORCE_INLINE(int) fsPerfIoWriteWorker(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t *pbBlock,
1904 uint64_t *poffActual, uint32_t *pcSeeks)
1905{
1906 /* Do we need to seek back to the start? */
1907 if (*poffActual + cbBlock <= cbFile)
1908 { /* likely */ }
1909 else
1910 {
1911 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1912 *pcSeeks += 1;
1913 *poffActual = 0;
1914 }
1915
1916 size_t cbActuallyWritten = 0;
1917 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBlock, cbBlock, &cbActuallyWritten), VINF_SUCCESS, rcCheck);
1918 if (cbActuallyWritten == cbBlock)
1919 {
1920 *poffActual += cbActuallyWritten;
1921 return VINF_SUCCESS;
1922 }
1923 RTTestIFailed("RTFileWrite at %#RX64 returned just %#x bytes, expected %#x", *poffActual, cbActuallyWritten, cbBlock);
1924 *poffActual += cbActuallyWritten;
1925 return VERR_WRITE_ERROR;
1926}
1927
1928
1929void fsPerfIoWriteBlockSize(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock)
1930{
1931 RTTestISubF("IO - Sequential write %RU32", cbBlock);
1932
1933 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBlock);
1934 if (pbBuf)
1935 {
1936 memset(pbBuf, 0xf7, cbBlock);
1937 PROFILE_IO_FN("RTFileWrite", fsPerfIoWriteWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks));
1938 RTMemPageFree(pbBuf, cbBlock);
1939 }
1940 else
1941 RTTestSkipped(g_hTest, "insufficient (virtual) memory available");
1942}
1943
1944
1945void fsPerfWrite(RTFILE hFile1, RTFILE hFileNoCache, RTFILE hFileWriteThru, uint64_t cbFile)
1946{
1947 RTTestISubF("IO - RTFileWrite");
1948
1949 /*
1950 * Allocate a big buffer we can play around with. Min size is 1MB.
1951 */
1952 size_t cbBuf = cbFile < _64M ? (size_t)cbFile : _64M;
1953 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
1954 while (!pbBuf)
1955 {
1956 cbBuf /= 2;
1957 RTTESTI_CHECK_RETV(cbBuf >= _1M);
1958 pbBuf = (uint8_t *)RTMemPageAlloc(_32M);
1959 }
1960
1961 /*
1962 * Start at the beginning and write out the full buffer in random small chunks, thereby
1963 * checking that unaligned buffer addresses, size and file offsets work fine.
1964 */
1965 struct
1966 {
1967 uint64_t offFile;
1968 uint32_t cbMax;
1969 } aRuns[] = { { 0, 127 }, { cbFile - cbBuf, UINT32_MAX }, { 0, UINT32_MAX -1 }};
1970 uint8_t bFiller = 0x88;
1971 for (uint32_t i = 0; i < RT_ELEMENTS(aRuns); i++, bFiller)
1972 {
1973 fsPerfFillWriteBuf(aRuns[i].offFile, pbBuf, cbBuf, bFiller);
1974 fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf, bFiller);
1975
1976 RTTESTI_CHECK_RC(RTFileSeek(hFile1, aRuns[i].offFile, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1977 for (size_t offBuf = 0; offBuf < cbBuf; )
1978 {
1979 uint32_t const cbLeft = (uint32_t)(cbBuf - offBuf);
1980 uint32_t const cbToWrite = aRuns[i].cbMax < UINT32_MAX / 2 ? RTRandU32Ex(1, aRuns[i].cbMax)
1981 : aRuns[i].cbMax == UINT32_MAX ? RTRandU32Ex(RT_MAX(cbLeft / 4, 1), cbLeft)
1982 : RTRandU32Ex(cbLeft >= _8K ? _8K : 1, RT_MIN(_1M, cbLeft));
1983 size_t cbActual = 0;
1984 RTTESTI_CHECK_RC(RTFileWrite(hFile1, &pbBuf[offBuf], cbToWrite, &cbActual), VINF_SUCCESS);
1985 if (cbActual == cbToWrite)
1986 offBuf += cbActual;
1987 else
1988 {
1989 RTTestIFailed("Attempting to write %#x bytes at %#zx, only got %#x written!\n", cbToWrite, offBuf, cbActual);
1990 if (cbActual)
1991 offBuf += cbActual;
1992 else
1993 pbBuf[offBuf++] = 0x11;
1994 }
1995 }
1996
1997 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, aRuns[i].offFile, pbBuf, cbBuf, NULL), VINF_SUCCESS);
1998 fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf, bFiller);
1999 }
2000
2001
2002 /*
2003 * Do uncached and write-thru accesses, must be page aligned.
2004 */
2005 RTFILE ahFiles[2] = { hFileWriteThru, hFileNoCache };
2006 for (unsigned iFile = 0; iFile < RT_ELEMENTS(ahFiles); iFile++, bFiller++)
2007 {
2008 fsPerfFillWriteBuf(0, pbBuf, cbBuf, bFiller);
2009 fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf, bFiller);
2010 RTTESTI_CHECK_RC(RTFileSeek(ahFiles[iFile], 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
2011
2012 uint32_t cbPage = PAGE_SIZE;
2013 for (size_t offBuf = 0; offBuf < cbBuf; )
2014 {
2015 uint32_t const cPagesLeft = (uint32_t)((cbBuf - offBuf) / cbPage);
2016 uint32_t const cPagesToWrite = RTRandU32Ex(1, cPagesLeft);
2017 size_t const cbToWrite = cPagesToWrite * (size_t)cbPage;
2018 size_t cbActual = 0;
2019 RTTESTI_CHECK_RC(RTFileWrite(ahFiles[iFile], &pbBuf[offBuf], cbToWrite, &cbActual), VINF_SUCCESS);
2020 if (cbActual == cbToWrite)
2021 {
2022 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, offBuf, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
2023 fsPerfCheckReadBuf(__LINE__, offBuf, pbBuf, cbToWrite, bFiller);
2024 offBuf += cbActual;
2025 }
2026 else
2027 {
2028 RTTestIFailed("Attempting to read %#zx bytes at %#zx, only got %#x written!\n", cbToWrite, offBuf, cbActual);
2029 if (cbActual)
2030 offBuf += cbActual;
2031 else
2032 {
2033 memset(&pbBuf[offBuf], 0x11, cbPage);
2034 offBuf += cbPage;
2035 }
2036 }
2037 }
2038
2039 RTTESTI_CHECK_RC(RTFileReadAt(ahFiles[iFile], 0, pbBuf, cbBuf, NULL), VINF_SUCCESS);
2040 fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf, bFiller);
2041 }
2042
2043 /*
2044 * Check the behavior of writing zero bytes to the file _4K from the end
2045 * using native API. In the olden days zero sized write have been known
2046 * to be used to truncate a file.
2047 */
2048 RTTESTI_CHECK_RC(RTFileSeek(hFile1, -_4K, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
2049#ifdef RT_OS_WINDOWS
2050 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2051 NTSTATUS rcNt = NtWriteFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 0, NULL, NULL);
2052 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
2053 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
2054 RTTESTI_CHECK(Ios.Information == 0);
2055
2056 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, _4K, NULL), VINF_SUCCESS);
2057 fsPerfCheckReadBuf(__LINE__, cbFile - _4K, pbBuf, _4K, pbBuf[0x8]);
2058#endif
2059
2060 /*
2061 * Other OS specific stuff.
2062 */
2063#ifdef RT_OS_WINDOWS
2064 /* Check that reading at an offset modifies the position: */
2065 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, cbFile / 2, pbBuf, _4K, NULL), VINF_SUCCESS);
2066 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
2067 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile);
2068
2069 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
2070 LARGE_INTEGER offNt;
2071 offNt.QuadPart = cbFile / 2;
2072 rcNt = NtWriteFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, _4K, &offNt, NULL);
2073 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
2074 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
2075 RTTESTI_CHECK(Ios.Information == _4K);
2076 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile / 2 + _4K);
2077#endif
2078
2079 RT_NOREF(hFileNoCache, hFileWriteThru);
2080 RTMemPageFree(pbBuf, cbBuf);
2081}
2082
2083
2084/**
2085 * Worker for testing RTFileFlush.
2086 */
2087DECL_FORCE_INLINE(int) fsPerfFSyncWorker(RTFILE hFile1, uint64_t cbFile, uint8_t *pbBuf, size_t cbBuf, uint64_t *poffFile)
2088{
2089 if (*poffFile + cbBuf <= cbFile)
2090 { /* likely */ }
2091 else
2092 {
2093 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
2094 *poffFile = 0;
2095 }
2096
2097 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBuf, cbBuf, NULL), VINF_SUCCESS, rcCheck);
2098 RTTESTI_CHECK_RC_RET(RTFileFlush(hFile1), VINF_SUCCESS, rcCheck);
2099
2100 *poffFile += cbBuf;
2101 return VINF_SUCCESS;
2102}
2103
2104
2105void fsPerfFSync(RTFILE hFile1, uint64_t cbFile)
2106{
2107 RTTestISub("fsync");
2108
2109 RTTESTI_CHECK_RC(RTFileFlush(hFile1), VINF_SUCCESS);
2110
2111 PROFILE_FN(RTFileFlush(hFile1), g_nsTestRun, "RTFileFlush");
2112
2113 size_t cbBuf = PAGE_SIZE;
2114 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
2115 RTTESTI_CHECK_RETV(pbBuf != NULL);
2116 memset(pbBuf, 0xf4, cbBuf);
2117
2118 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
2119 uint64_t offFile = 0;
2120 PROFILE_FN(fsPerfFSyncWorker(hFile1, cbFile, pbBuf, cbBuf, &offFile), g_nsTestRun, "RTFileWrite[Page]/RTFileFlush");
2121
2122 RTMemPageFree(pbBuf, cbBuf);
2123}
2124
2125
2126void fsPerfMMap(RTFILE hFile1, RTFILE hFileNoCache, uint64_t cbFile)
2127{
2128 RTTestISub("mmap");
2129#if defined(RT_OS_WINDOWS)
2130 static const char * const s_apszStates[] = { "readonly", "writecopy", "readwrite" };
2131 enum { kMMap_ReadOnly = 0, kMMap_WriteCopy, kMMap_ReadWrite, kMMap_End };
2132 for (int enmState = kMMap_ReadOnly; enmState < kMMap_End; enmState++)
2133 {
2134 /*
2135 * Do the mapping.
2136 */
2137 size_t cbMapping = (size_t)cbFile;
2138 if (cbMapping != cbFile)
2139 cbMapping = _256M;
2140 uint8_t *pbMapping;
2141
2142# ifdef RT_OS_WINDOWS
2143 HANDLE hSection;
2144 for (;; cbMapping /= 2)
2145 {
2146 hSection = CreateFileMapping((HANDLE)RTFileToNative(hFile1), NULL,
2147 enmState == kMMap_ReadOnly ? PAGE_READONLY
2148 : enmState == kMMap_WriteCopy ? PAGE_WRITECOPY : PAGE_READWRITE,
2149 (uint32_t)((uint64_t)cbMapping >> 32), (uint32_t)cbMapping, NULL);
2150 DWORD dwErr1 = GetLastError();
2151 DWORD dwErr2 = 0;
2152 if (hSection != NULL)
2153 {
2154 pbMapping = (uint8_t *)MapViewOfFile(hSection,
2155 enmState == kMMap_ReadOnly ? FILE_MAP_READ
2156 : enmState == kMMap_WriteCopy ? FILE_MAP_COPY
2157 : FILE_MAP_WRITE,
2158 0, 0, cbMapping);
2159 if (pbMapping)
2160 break;
2161 dwErr2 = GetLastError();
2162 CloseHandle(hSection);
2163 }
2164 if (cbMapping <= _2M)
2165 {
2166 RTTestIFailed("%u: CreateFileMapping or MapViewOfFile failed: %u, %u", enmState, dwErr1, dwErr2);
2167 return;
2168 }
2169 }
2170# else
2171 for (;; cbMapping /= 2)
2172 {
2173 pbMapping = (uint8_t *)mmap(NULL, cbMapping,
2174 enmState == kMMap_ReadOnly ? PROT_READ : PROT_READ | PROT_WRITE,
2175 enmState == kMMap_WriteCopy ? MAP_PRIVATE : MAP_SHARED,
2176 (int)RTFileToNative(hFile1), cbMapping);
2177 if ((void *)pbMapping != MAP_FAILED)
2178 break;
2179 RTTESTI_CHECK_MSG_RETV(cbMapping > _2M, ("errno=%d", errno));
2180 }
2181# endif
2182
2183 /*
2184 * Time page-ins just for fun.
2185 */
2186 size_t const cPages = cbMapping >> PAGE_SHIFT;
2187 size_t uDummy = 0;
2188 uint64_t ns = RTTimeNanoTS();
2189 for (size_t iPage = 0; iPage < cPages; iPage++)
2190 uDummy += ASMAtomicReadU8(&pbMapping[iPage << PAGE_SHIFT]);
2191 ns = RTTimeNanoTS() - ns;
2192 RTTestIValueF(ns / cPages, RTTESTUNIT_NS_PER_OCCURRENCE, "page-in %s", s_apszStates[enmState]);
2193
2194 /* Check the content. */
2195 fsPerfCheckReadBuf(__LINE__, 0, pbMapping, cbMapping);
2196
2197 if (enmState != kMMap_ReadOnly)
2198 {
2199 /* Write stuff to the first two megabytes. In the COW case, we'll detect
2200 corruption of shared data during content checking of the RW iterations. */
2201 fsPerfFillWriteBuf(0, pbMapping, _2M, 0xf7);
2202 if (enmState == kMMap_ReadWrite)
2203 {
2204 /* For RW we can try read back from the file handle and check if we get
2205 a match there first. */
2206 uint8_t abBuf[_4K];
2207 for (uint32_t off = 0; off < _2M; off += sizeof(abBuf))
2208 {
2209 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, off, abBuf, sizeof(abBuf), NULL), VINF_SUCCESS);
2210 fsPerfCheckReadBuf(__LINE__, off, abBuf, sizeof(abBuf), 0xf7);
2211 }
2212# ifdef RT_OS_WINDOWS
2213 RTTESTI_CHECK(FlushViewOfFile(pbMapping, _2M));
2214# else
2215 RTTESTI_CHECK(msync(pbMapping, _2M, MS_SYNC) == 0);
2216# endif
2217
2218 /*
2219 * Time modifying and flushing a few different number of pages.
2220 */
2221 static size_t const s_acbFlush[] = { PAGE_SIZE, PAGE_SIZE * 2, PAGE_SIZE * 3, PAGE_SIZE * 8, PAGE_SIZE * 16, _2M };
2222 for (unsigned iFlushSize = 0 ; iFlushSize < RT_ELEMENTS(s_acbFlush); iFlushSize++)
2223 {
2224 size_t const cbFlush = s_acbFlush[iFlushSize];
2225 if (cbFlush > cbMapping)
2226 continue;
2227
2228 size_t const cFlushes = cbMapping / cbFlush;
2229 uint8_t *pbCur = pbMapping;
2230 ns = RTTimeNanoTS();
2231 for (size_t iFlush = 0; iFlush < cFlushes; iFlush++, pbCur += cbFlush)
2232 {
2233 for (size_t offFlush = 0; offFlush < cbFlush; offFlush += PAGE_SIZE)
2234 *(size_t volatile *)&pbCur[offFlush + 8] = cbFlush;
2235# ifdef RT_OS_WINDOWS
2236 RTTESTI_CHECK(FlushViewOfFile(pbCur, cbFlush));
2237# else
2238 RTTESTI_CHECK(msync(pbCur, cbFlush, MS_SYNC) == 0);
2239# endif
2240 }
2241 ns = RTTimeNanoTS() - ns;
2242 RTTestIValueF(ns / cFlushes, RTTESTUNIT_NS_PER_OCCURRENCE, "touch/flush/%zu", cbFlush);
2243
2244 /*
2245 * Check that all the changes made it thru to the file:
2246 */
2247 size_t cbBuf = _2M;
2248 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(_2M);
2249 if (!pbBuf)
2250 {
2251 cbBuf = _4K;
2252 pbBuf = (uint8_t *)RTMemPageAlloc(_2M);
2253 }
2254 if (pbBuf)
2255 {
2256 RTTESTI_CHECK_RC(RTFileSeek(hFileNoCache, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
2257 size_t const cbToCheck = cFlushes * cbFlush;
2258 unsigned cErrors = 0;
2259 for (size_t offBuf = 0; cErrors < 32 && offBuf < cbToCheck; offBuf += cbBuf)
2260 {
2261 size_t cbToRead = RT_MIN(cbBuf, cbToCheck - offBuf);
2262 RTTESTI_CHECK_RC(RTFileRead(hFileNoCache, pbBuf, cbToRead, NULL), VINF_SUCCESS);
2263
2264 for (size_t offFlush = 0; offFlush < cbToRead; offFlush += PAGE_SIZE)
2265 if (*(size_t volatile *)&pbBuf[offFlush + 8] != cbFlush)
2266 {
2267 RTTestIFailed("Flush issue at offset #%zx: %#zx, expected %#zx (cbFlush=%#zx)",
2268 offBuf, *(size_t volatile *)&pbBuf[offFlush + 8], cbFlush, cbFlush);
2269 if (++cErrors > 32)
2270 break;
2271 }
2272 }
2273 RTMemPageFree(pbBuf, cbBuf);
2274 }
2275 }
2276 }
2277 }
2278
2279 /*
2280 * Unmap it.
2281 */
2282# ifdef RT_OS_WINDOWS
2283 RTTESTI_CHECK(UnmapViewOfFile(pbMapping));
2284 RTTESTI_CHECK(CloseHandle(hSection));
2285# else
2286 RTTESTI_CHECK(munmap(pbMapping, cbMapping) == 0);
2287# endif
2288 }
2289
2290 RT_NOREF(hFileNoCache);
2291#else
2292 RTTestSkipped(g_hTest, "not supported/implemented");
2293 RT_NOREF(hFile1, hFileNoCache, cbFile);
2294#endif
2295}
2296
2297
2298/**
2299 * This does the read, write and seek tests.
2300 */
2301void fsPerfIo(void)
2302{
2303 RTTestISub("I/O");
2304
2305 /*
2306 * Determin the size of the test file.
2307 */
2308 g_szDir[g_cchDir] = '\0';
2309 RTFOFF cbFree = 0;
2310 RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
2311 uint64_t cbFile = g_cbIoFile;
2312 if (cbFile + _16M < (uint64_t)cbFree)
2313 cbFile = RT_ALIGN_64(cbFile, _64K);
2314 else
2315 {
2316 if (cbFree < _32M)
2317 {
2318 RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 32MB", cbFree);
2319 return;
2320 }
2321 cbFile = cbFree - (cbFree > _128M ? _64M : _16M);
2322 cbFile = RT_ALIGN_64(cbFile, _64K);
2323 RTTestIPrintf(RTTESTLVL_ALWAYS, "Adjusted file size to %'RU64 bytes, due to %'RU64 bytes free.\n", cbFile, cbFree);
2324 }
2325 if (cbFile < _64K)
2326 {
2327 RTTestSkipped(g_hTest, "Specified test file size too small: %'RU64 bytes, requires >= 64KB", cbFile);
2328 return;
2329 }
2330
2331 /*
2332 * Create a cbFile sized test file.
2333 */
2334 RTFILE hFile1;
2335 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file21")),
2336 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
2337 RTFILE hFileNoCache;
2338 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFileNoCache, g_szDir,
2339 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE | RTFILE_O_NO_CACHE),
2340 VINF_SUCCESS);
2341 RTFILE hFileWriteThru;
2342 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFileWriteThru, g_szDir,
2343 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE | RTFILE_O_WRITE_THROUGH),
2344 VINF_SUCCESS);
2345
2346 uint8_t *pbFree = NULL;
2347 int rc = fsPerfIoPrepFile(hFile1, cbFile, &pbFree);
2348 RTMemFree(pbFree);
2349 if (RT_SUCCESS(rc))
2350 {
2351 /*
2352 * Do the testing & profiling.
2353 */
2354 if (g_fSeek)
2355 fsPerfIoSeek(hFile1, cbFile);
2356 if (g_fRead)
2357 {
2358 fsPerfRead(hFile1, hFileNoCache, cbFile);
2359 for (unsigned i = 0; i < g_cIoBlocks; i++)
2360 fsPerfIoReadBlockSize(hFile1, cbFile, g_acbIoBlocks[i]);
2361 }
2362 if (g_fMMap)
2363 fsPerfMMap(hFile1, hFileNoCache, cbFile);
2364 if (g_fWrite)
2365 {
2366 /* This is destructive to the file content. */
2367 fsPerfWrite(hFile1, hFileNoCache, hFileWriteThru, cbFile);
2368 for (unsigned i = 0; i < g_cIoBlocks; i++)
2369 fsPerfIoWriteBlockSize(hFile1, cbFile, g_acbIoBlocks[i]);
2370 }
2371 if (g_fFSync)
2372 fsPerfFSync(hFile1, cbFile);
2373 }
2374
2375 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS);
2376 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2377 RTTESTI_CHECK_RC(RTFileClose(hFileNoCache), VINF_SUCCESS);
2378 RTTESTI_CHECK_RC(RTFileClose(hFileWriteThru), VINF_SUCCESS);
2379 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
2380}
2381
2382
2383static void Usage(PRTSTREAM pStrm)
2384{
2385 char szExec[RTPATH_MAX];
2386 RTStrmPrintf(pStrm, "usage: %s <-d <testdir>> [options]\n",
2387 RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
2388 RTStrmPrintf(pStrm, "\n");
2389 RTStrmPrintf(pStrm, "options: \n");
2390
2391 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
2392 {
2393 char szHelp[80];
2394 const char *pszHelp;
2395 switch (g_aCmdOptions[i].iShort)
2396 {
2397 case 'd': pszHelp = "The directory to use for testing. Default: CWD/fstestdir"; break;
2398 case 'e': pszHelp = "Enables all tests. Default: -e"; break;
2399 case 'z': pszHelp = "Disables all tests. Default: -e"; break;
2400 case 's': pszHelp = "Set benchmark duration in seconds. Default: 10 sec"; break;
2401 case 'm': pszHelp = "Set benchmark duration in milliseconds. Default: 10000 ms"; break;
2402 case 'v': pszHelp = "More verbose execution."; break;
2403 case 'q': pszHelp = "Quiet execution."; break;
2404 case 'h': pszHelp = "Displays this help and exit"; break;
2405 case 'V': pszHelp = "Displays the program revision"; break;
2406 default:
2407 if (g_aCmdOptions[i].iShort >= kCmdOpt_First)
2408 {
2409 if (RTStrStartsWith(g_aCmdOptions[i].pszLong, "--no-"))
2410 RTStrPrintf(szHelp, sizeof(szHelp), "Disables the '%s' test.", g_aCmdOptions[i].pszLong + 5);
2411 else
2412 RTStrPrintf(szHelp, sizeof(szHelp), "Enables the '%s' test.", g_aCmdOptions[i].pszLong + 2);
2413 pszHelp = szHelp;
2414 }
2415 else
2416 pszHelp = "Option undocumented";
2417 break;
2418 }
2419 if ((unsigned)g_aCmdOptions[i].iShort < 127U)
2420 {
2421 char szOpt[64];
2422 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
2423 RTStrmPrintf(pStrm, " %-20s%s\n", szOpt, pszHelp);
2424 }
2425 else
2426 RTStrmPrintf(pStrm, " %-20s%s\n", g_aCmdOptions[i].pszLong, pszHelp);
2427 }
2428}
2429
2430
2431int main(int argc, char *argv[])
2432{
2433 /*
2434 * Init IPRT and globals.
2435 */
2436 int rc = RTTestInitAndCreate("FsPerf", &g_hTest);
2437 if (rc)
2438 return rc;
2439 RTListInit(&g_ManyTreeHead);
2440
2441 /*
2442 * Default values.
2443 */
2444 rc = RTPathGetCurrent(g_szDir, sizeof(g_szDir) / 2);
2445 if (RT_SUCCESS(rc))
2446 rc = RTPathAppend(g_szDir, sizeof(g_szDir) / 2, "fstestdir-");
2447 if (RT_SUCCESS(rc))
2448 {
2449 g_cchDir = strlen(g_szDir);
2450 g_cchDir += RTStrPrintf(&g_szDir[g_cchDir], sizeof(g_szDir) - g_cchDir, "%u" RTPATH_SLASH_STR, RTProcSelf());
2451 }
2452 else
2453 {
2454 RTTestFailed(g_hTest, "RTPathGetCurrent (or RTPathAppend) failed: %Rrc\n", rc);
2455 return RTTestSummaryAndDestroy(g_hTest);
2456 }
2457
2458 RTGETOPTUNION ValueUnion;
2459 RTGETOPTSTATE GetState;
2460 RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
2461 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2462 {
2463 switch (rc)
2464 {
2465 case 'd':
2466 rc = RTPathAbs(ValueUnion.psz, g_szDir, sizeof(g_szDir) / 2);
2467 if (RT_SUCCESS(rc))
2468 break;
2469 RTTestFailed(g_hTest, "RTPathAbs(%s) failed: %Rrc\n", ValueUnion.psz, rc);
2470 return RTTestSummaryAndDestroy(g_hTest);
2471
2472 case 's':
2473 if (ValueUnion.u32 == 0)
2474 g_nsTestRun = RT_NS_1SEC_64 * 10;
2475 else
2476 g_nsTestRun = ValueUnion.u32 * RT_NS_1SEC_64;
2477 break;
2478
2479 case 'm':
2480 if (ValueUnion.u64 == 0)
2481 g_nsTestRun = RT_NS_1SEC_64 * 10;
2482 else
2483 g_nsTestRun = ValueUnion.u64 * RT_NS_1MS;
2484 break;
2485
2486 case 'e':
2487 g_fManyFiles = true;
2488 g_fOpen = true;
2489 g_fFStat = true;
2490 g_fFChMod = true;
2491 g_fFUtimes = true;
2492 g_fStat = true;
2493 g_fChMod = true;
2494 g_fUtimes = true;
2495 g_fRename = true;
2496 g_fDirEnum = true;
2497 g_fMkRmDir = true;
2498 g_fStatVfs = true;
2499 g_fRm = true;
2500 g_fChSize = true;
2501 g_fRead = true;
2502 g_fWrite = true;
2503 g_fSeek = true;
2504 g_fFSync = true;
2505 g_fMMap = true;
2506 break;
2507
2508 case 'z':
2509 g_fManyFiles = false;
2510 g_fOpen = false;
2511 g_fFStat = false;
2512 g_fFChMod = false;
2513 g_fFUtimes = false;
2514 g_fStat = false;
2515 g_fChMod = false;
2516 g_fUtimes = false;
2517 g_fRename = false;
2518 g_fDirEnum = false;
2519 g_fMkRmDir = false;
2520 g_fStatVfs = false;
2521 g_fRm = false;
2522 g_fChSize = false;
2523 g_fRead = false;
2524 g_fWrite = false;
2525 g_fSeek = false;
2526 g_fFSync = false;
2527 g_fMMap = false;
2528 break;
2529
2530#define CASE_OPT(a_Stem) \
2531 case RT_CONCAT(kCmdOpt_,a_Stem): RT_CONCAT(g_f,a_Stem) = true; break; \
2532 case RT_CONCAT(kCmdOpt_No,a_Stem): RT_CONCAT(g_f,a_Stem) = false; break
2533 CASE_OPT(ManyFiles);
2534 CASE_OPT(Open);
2535 CASE_OPT(FStat);
2536 CASE_OPT(FChMod);
2537 CASE_OPT(FUtimes);
2538 CASE_OPT(Stat);
2539 CASE_OPT(ChMod);
2540 CASE_OPT(Utimes);
2541 CASE_OPT(Rename);
2542 CASE_OPT(DirEnum);
2543 CASE_OPT(MkRmDir);
2544 CASE_OPT(StatVfs);
2545 CASE_OPT(Rm);
2546 CASE_OPT(ChSize);
2547 CASE_OPT(Read);
2548 CASE_OPT(Write);
2549 CASE_OPT(Seek);
2550 CASE_OPT(FSync);
2551 CASE_OPT(MMap);
2552#undef CASE_OPT
2553
2554 case 'q':
2555 g_uVerbosity = 0;
2556 break;
2557
2558 case 'v':
2559 g_uVerbosity++;
2560 break;
2561
2562 case 'h':
2563 Usage(g_pStdOut);
2564 return RTEXITCODE_SUCCESS;
2565
2566 case 'V':
2567 {
2568 char szRev[] = "$Revision: 76918 $";
2569 szRev[RT_ELEMENTS(szRev) - 2] = '\0';
2570 RTPrintf(RTStrStrip(strchr(szRev, ':') + 1));
2571 return RTEXITCODE_SUCCESS;
2572 }
2573
2574 default:
2575 return RTGetOptPrintError(rc, &ValueUnion);
2576 }
2577 }
2578
2579 /*
2580 * Create the test directory with an 'empty' subdirectory under it,
2581 * execute the tests, and remove directory when done.
2582 */
2583 RTTestBanner(g_hTest);
2584 if (!RTPathExists(g_szDir))
2585 {
2586 /* The base dir: */
2587 rc = RTDirCreate(g_szDir, 0755,
2588 RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
2589 if (RT_SUCCESS(rc))
2590 {
2591 RTTestIPrintf(RTTESTLVL_ALWAYS, "Test dir: %s\n", g_szDir);
2592 rc = fsPrepTestArea();
2593 if (RT_SUCCESS(rc))
2594 {
2595 /* Profile RTTimeNanoTS(). */
2596 fsPerfNanoTS();
2597
2598 /* Do tests: */
2599 if (g_fManyFiles)
2600 fsPerfManyFiles();
2601 if (g_fOpen)
2602 fsPerfOpen();
2603 if (g_fFStat)
2604 fsPerfFStat();
2605 if (g_fFChMod)
2606 fsPerfFChMod();
2607 if (g_fFUtimes)
2608 fsPerfFUtimes();
2609 if (g_fStat)
2610 fsPerfStat();
2611 if (g_fChMod)
2612 fsPerfChmod();
2613 if (g_fUtimes)
2614 fsPerfUtimes();
2615 if (g_fRename)
2616 fsPerfRename();
2617 if (g_fDirEnum)
2618 vsPerfDirEnum();
2619 if (g_fMkRmDir)
2620 fsPerfMkRmDir();
2621 if (g_fStatVfs)
2622 fsPerfStatVfs();
2623 if (g_fRm || g_fManyFiles)
2624 fsPerfRm(); /* deletes manyfiles and manytree */
2625 if (g_fChSize)
2626 fsPerfChSize();
2627 if (g_fRead || g_fWrite || g_fSeek || g_fFSync || g_fMMap)
2628 fsPerfIo();
2629 }
2630
2631 /* Cleanup: */
2632 g_szDir[g_cchDir] = '\0';
2633 rc = RTDirRemoveRecursive(g_szDir, RTDIRRMREC_F_CONTENT_AND_DIR);
2634 if (RT_FAILURE(rc))
2635 RTTestFailed(g_hTest, "RTDirRemoveRecursive(%s,) -> %Rrc\n", g_szDir, rc);
2636 }
2637 else
2638 RTTestFailed(g_hTest, "RTDirCreate(%s) -> %Rrc\n", g_szDir, rc);
2639 }
2640 else
2641 RTTestFailed(g_hTest, "Test directory already exists: %s\n", g_szDir);
2642
2643 return RTTestSummaryAndDestroy(g_hTest);
2644}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette