VirtualBox

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

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

FsPerf: Updates. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.7 KB
Line 
1/* $Id: FsPerf.cpp 76897 2019-01-19 06:56:27Z 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/path.h>
43#include <iprt/process.h>
44#include <iprt/rand.h>
45#include <iprt/string.h>
46#include <iprt/stream.h>
47#include <iprt/test.h>
48#include <iprt/time.h>
49#include <iprt/thread.h>
50#include <iprt/zero.h>
51
52
53/*********************************************************************************************************************************
54* Defined Constants And Macros *
55*********************************************************************************************************************************/
56/**
57 * Macro for profiling @a a_fnCall (typically forced inline) for about @a a_cNsTarget ns.
58 *
59 * Always does an even number of iterations.
60 */
61#define PROFILE_FN(a_fnCall, a_cNsTarget, a_szDesc) \
62 do { \
63 /* Estimate how many iterations we need to fill up the given timeslot: */ \
64 fsPerfYield(); \
65 uint64_t nsStart = RTTimeNanoTS(); \
66 uint64_t ns; \
67 do \
68 ns = RTTimeNanoTS(); \
69 while (ns == nsStart); \
70 nsStart = ns; \
71 \
72 uint64_t iIteration = 0; \
73 do \
74 { \
75 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
76 iIteration++; \
77 ns = RTTimeNanoTS() - nsStart; \
78 } while (ns < RT_NS_10MS || (iIteration & 1)); \
79 ns /= iIteration; \
80 if (ns > g_nsPerNanoTSCall + 32) \
81 ns -= g_nsPerNanoTSCall; \
82 \
83 uint64_t cIterations = (a_cNsTarget) / ns; \
84 if (cIterations <= 1) \
85 cIterations = 2; \
86 else if (cIterations & 1) \
87 cIterations++; \
88 \
89 /* Do the actual profiling: */ \
90 fsPerfYield(); \
91 iIteration = 0; \
92 nsStart = RTTimeNanoTS(); \
93 for (; iIteration < cIterations; iIteration++) \
94 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
95 ns = RTTimeNanoTS() - nsStart; \
96 RTTestIValueF(ns / cIterations, RTTESTUNIT_NS_PER_OCCURRENCE, a_szDesc); \
97 if (g_fShowDuration) \
98 RTTestIValueF(ns, RTTESTUNIT_NS, "%s duration", a_szDesc); \
99 } while (0)
100
101
102/**
103 * Macro for profiling an operation on each file in the manytree directory tree.
104 *
105 * Always does an even number of tree iterations.
106 */
107#define PROFILE_MANYTREE_FN(a_szPath, a_fnCall, a_cEstimationIterations, a_cNsTarget, a_szDesc) \
108 do { \
109 if (!g_fManyFiles) \
110 break; \
111 \
112 /* Estimate how many iterations we need to fill up the given timeslot: */ \
113 fsPerfYield(); \
114 uint64_t nsStart = RTTimeNanoTS(); \
115 uint64_t ns; \
116 do \
117 ns = RTTimeNanoTS(); \
118 while (ns == nsStart); \
119 nsStart = ns; \
120 \
121 PFSPERFNAMEENTRY pCur; \
122 uint64_t iIteration = 0; \
123 do \
124 { \
125 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
126 { \
127 memcpy(a_szPath, pCur->szName, pCur->cchName); \
128 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
129 { \
130 RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
131 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
132 } \
133 } \
134 iIteration++; \
135 ns = RTTimeNanoTS() - nsStart; \
136 } while (ns < RT_NS_10MS || (iIteration & 1)); \
137 ns /= iIteration; \
138 if (ns > g_nsPerNanoTSCall + 32) \
139 ns -= g_nsPerNanoTSCall; \
140 \
141 uint32_t cIterations = (a_cNsTarget) / ns; \
142 if (cIterations <= 1) \
143 cIterations = 2; \
144 else if (cIterations & 1) \
145 cIterations++; \
146 \
147 /* Do the actual profiling: */ \
148 fsPerfYield(); \
149 uint32_t cCalls = 0; \
150 nsStart = RTTimeNanoTS(); \
151 for (iIteration = 0; iIteration < cIterations; iIteration++) \
152 { \
153 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
154 { \
155 memcpy(a_szPath, pCur->szName, pCur->cchName); \
156 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
157 { \
158 RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
159 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
160 cCalls++; \
161 } \
162 } \
163 } \
164 ns = RTTimeNanoTS() - nsStart; \
165 RTTestIValueF(ns / cCalls, RTTESTUNIT_NS_PER_OCCURRENCE, a_szDesc); \
166 if (g_fShowDuration) \
167 RTTestIValueF(ns, RTTESTUNIT_NS, "%s duration", a_szDesc); \
168 } while (0)
169
170
171/**
172 * Execute a_fnCall for each file in the manytree.
173 */
174#define DO_MANYTREE_FN(a_szPath, a_fnCall) \
175 do { \
176 PFSPERFNAMEENTRY pCur; \
177 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
178 { \
179 memcpy(a_szPath, pCur->szName, pCur->cchName); \
180 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
181 { \
182 RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
183 a_fnCall; \
184 } \
185 } \
186 } while (0)
187
188
189/** @def FSPERF_VERR_PATH_NOT_FOUND
190 * Hides the fact that we only get VERR_PATH_NOT_FOUND on non-unix systems. */
191#if defined(RT_OS_WINDOWS) //|| defined(RT_OS_OS2) - using posix APIs IIRC, so lost in translation.
192# define FSPERF_VERR_PATH_NOT_FOUND VERR_PATH_NOT_FOUND
193#else
194# define FSPERF_VERR_PATH_NOT_FOUND VERR_FILE_NOT_FOUND
195#endif
196
197
198/*********************************************************************************************************************************
199* Structures and Typedefs *
200*********************************************************************************************************************************/
201typedef struct FSPERFNAMEENTRY
202{
203 RTLISTNODE Entry;
204 uint16_t cchName;
205 char szName[RT_FLEXIBLE_ARRAY];
206} FSPERFNAMEENTRY;
207typedef FSPERFNAMEENTRY *PFSPERFNAMEENTRY;
208
209
210enum
211{
212 kCmdOpt_First = 128,
213
214 kCmdOpt_ManyFiles = kCmdOpt_First,
215 kCmdOpt_NoManyFiles,
216 kCmdOpt_Open,
217 kCmdOpt_NoOpen,
218 kCmdOpt_FStat,
219 kCmdOpt_NoFStat,
220 kCmdOpt_FChMod,
221 kCmdOpt_NoFChMod,
222 kCmdOpt_FUtimes,
223 kCmdOpt_NoFUtimes,
224 kCmdOpt_Stat,
225 kCmdOpt_NoStat,
226 kCmdOpt_ChMod,
227 kCmdOpt_NoChMod,
228 kCmdOpt_Utimes,
229 kCmdOpt_NoUtimes,
230 kCmdOpt_Rename,
231 kCmdOpt_NoRename,
232 kCmdOpt_DirEnum,
233 kCmdOpt_NoDirEnum,
234 kCmdOpt_MkRmDir,
235 kCmdOpt_NoMkRmDir,
236 kCmdOpt_StatVfs,
237 kCmdOpt_NoStatVfs,
238 kCmdOpt_Rm,
239 kCmdOpt_NoRm,
240 kCmdOpt_ChSize,
241 kCmdOpt_NoChSize,
242 kCmdOpt_Read,
243 kCmdOpt_NoRead,
244 kCmdOpt_Write,
245 kCmdOpt_NoWrite,
246 kCmdOpt_Seek,
247 kCmdOpt_NoSeek,
248
249 kCmdOpt_End
250};
251
252/*********************************************************************************************************************************
253* Global Variables *
254*********************************************************************************************************************************/
255/** Command line parameters */
256static const RTGETOPTDEF g_aCmdOptions[] =
257{
258 { "--dir", 'd', RTGETOPT_REQ_STRING },
259 { "--seconds", 's', RTGETOPT_REQ_UINT32 },
260 { "--milliseconds", 'm', RTGETOPT_REQ_UINT64 },
261
262 { "--enable-all", 'e', RTGETOPT_REQ_NOTHING },
263 { "--disable-all", 'z', RTGETOPT_REQ_NOTHING },
264
265 { "--many-files", kCmdOpt_ManyFiles, RTGETOPT_REQ_NOTHING },
266 { "--no-many-files", kCmdOpt_NoManyFiles, RTGETOPT_REQ_NOTHING },
267
268 { "--open", kCmdOpt_Open, RTGETOPT_REQ_NOTHING },
269 { "--no-open", kCmdOpt_NoOpen, RTGETOPT_REQ_NOTHING },
270 { "--fstat", kCmdOpt_FStat, RTGETOPT_REQ_NOTHING },
271 { "--no-fstat", kCmdOpt_NoFStat, RTGETOPT_REQ_NOTHING },
272 { "--fchmod", kCmdOpt_FChMod, RTGETOPT_REQ_NOTHING },
273 { "--no-fchmod", kCmdOpt_NoFChMod, RTGETOPT_REQ_NOTHING },
274 { "--futimes", kCmdOpt_FUtimes, RTGETOPT_REQ_NOTHING },
275 { "--no-futimes", kCmdOpt_NoFUtimes, RTGETOPT_REQ_NOTHING },
276 { "--stat", kCmdOpt_Stat, RTGETOPT_REQ_NOTHING },
277 { "--no-stat", kCmdOpt_NoStat, RTGETOPT_REQ_NOTHING },
278 { "--chmod", kCmdOpt_ChMod, RTGETOPT_REQ_NOTHING },
279 { "--no-chmod", kCmdOpt_NoChMod, RTGETOPT_REQ_NOTHING },
280 { "--utimes", kCmdOpt_Utimes, RTGETOPT_REQ_NOTHING },
281 { "--no-utimes", kCmdOpt_NoUtimes, RTGETOPT_REQ_NOTHING },
282 { "--rename", kCmdOpt_Rename, RTGETOPT_REQ_NOTHING },
283 { "--no-rename", kCmdOpt_NoRename, RTGETOPT_REQ_NOTHING },
284 { "--dir-enum", kCmdOpt_DirEnum, RTGETOPT_REQ_NOTHING },
285 { "--no-dir-enum", kCmdOpt_NoDirEnum, RTGETOPT_REQ_NOTHING },
286 { "--mk-rm-dir", kCmdOpt_MkRmDir, RTGETOPT_REQ_NOTHING },
287 { "--no-mk-rm-dir", kCmdOpt_NoMkRmDir, RTGETOPT_REQ_NOTHING },
288 { "--stat-vfs", kCmdOpt_StatVfs, RTGETOPT_REQ_NOTHING },
289 { "--no-stat-vfs", kCmdOpt_NoStatVfs, RTGETOPT_REQ_NOTHING },
290 { "--rm", kCmdOpt_Rm, RTGETOPT_REQ_NOTHING },
291 { "--no-rm", kCmdOpt_NoRm, RTGETOPT_REQ_NOTHING },
292 { "--chsize", kCmdOpt_ChSize, RTGETOPT_REQ_NOTHING },
293 { "--no-chsize", kCmdOpt_NoChSize, RTGETOPT_REQ_NOTHING },
294 { "--read", kCmdOpt_Read, RTGETOPT_REQ_NOTHING },
295 { "--no-read", kCmdOpt_NoRead, RTGETOPT_REQ_NOTHING },
296 { "--write", kCmdOpt_Write, RTGETOPT_REQ_NOTHING },
297 { "--no-write", kCmdOpt_NoWrite, RTGETOPT_REQ_NOTHING },
298 { "--seek", kCmdOpt_Seek, RTGETOPT_REQ_NOTHING },
299 { "--no-seek", kCmdOpt_NoSeek, RTGETOPT_REQ_NOTHING },
300
301 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
302 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
303 { "--version", 'V', RTGETOPT_REQ_NOTHING },
304 { "--help", 'h', RTGETOPT_REQ_NOTHING } /* for Usage() */
305};
306
307/** The test handle. */
308static RTTEST g_hTest;
309/** The number of nanoseconds a RTTimeNanoTS call takes.
310 * This is used for adjusting loop count estimates. */
311static uint64_t g_nsPerNanoTSCall = 1;
312/** Whether or not to display the duration of each profile run.
313 * This is chiefly for verify the estimate phase. */
314static bool g_fShowDuration = true;
315/** Verbosity level. */
316static uint32_t g_uVerbosity = 0;
317
318/** @name Selected subtest
319 * @{ */
320static bool g_fManyFiles = true;
321static bool g_fOpen = true;
322static bool g_fFStat = true;
323static bool g_fFChMod = true;
324static bool g_fFUtimes = true;
325static bool g_fStat = true;
326static bool g_fChMod = true;
327static bool g_fUtimes = true;
328static bool g_fRename = true;
329static bool g_fDirEnum = true;
330static bool g_fMkRmDir = true;
331static bool g_fStatVfs = true;
332static bool g_fRm = true;
333static bool g_fChSize = true;
334static bool g_fSeek = true;
335static bool g_fRead = true;
336static bool g_fWrite = true;
337/** @} */
338
339/** The length of each test run. */
340static uint64_t g_nsTestRun = RT_NS_1SEC_64 * 10;
341
342/** For the 'manyfiles' subdir. */
343static uint32_t g_cManyFiles = 10000;
344
345/** Number of files in the 'manytree' directory tree. */
346static uint32_t g_cManyTreeFiles = 640 + 16*640 /*10880*/;
347/** Number of files per directory in the 'manytree' construct. */
348static uint32_t g_cManyTreeFilesPerDir = 640;
349/* Number of subdirs per directory in the 'manytree' construct. */
350static uint32_t g_cManyTreeSubdirsPerDir = 16;
351/** The depth of the 'manytree' directory tree. */
352static uint32_t g_cManyTreeDepth = 1;
353/** List of directories in the many tree, creation order. */
354static RTLISTANCHOR g_ManyTreeHead;
355
356/** Number of configured I/O block sizes. */
357static uint32_t g_cIoBlocks = 8;
358/** Configured I/O block sizes. */
359static uint32_t g_acbIoBlocks[16] = { 1, 512, 4096, 16384, 65536, _1M, _32M, _128M };
360/** The desired size of the test file we use for I/O. */
361static uint64_t g_cbIoFile = _512M;
362
363/** The length of g_szDir. */
364static size_t g_cchDir;
365/** The length of g_szEmptyDir. */
366static size_t g_cchEmptyDir;
367/** The length of g_szDeepDir. */
368static size_t g_cchDeepDir;
369
370/** The test directory (absolute). This will always have a trailing slash. */
371static char g_szDir[RTPATH_MAX];
372/** The empty test directory (absolute). This will always have a trailing slash. */
373static char g_szEmptyDir[RTPATH_MAX];
374/** The deep test directory (absolute). This will always have a trailing slash. */
375static char g_szDeepDir[RTPATH_MAX];
376
377
378/**
379 * Yield the CPU and stuff before starting a test run.
380 */
381DECLINLINE(void) fsPerfYield(void)
382{
383 RTThreadYield();
384 RTThreadYield();
385}
386
387
388/**
389 * Profiles the RTTimeNanoTS call, setting g_nsPerNanoTSCall.
390 */
391static void fsPerfNanoTS(void)
392{
393 fsPerfYield();
394
395 /* Make sure we start off on a changing timestamp on platforms will low time resoultion. */
396 uint64_t nsStart = RTTimeNanoTS();
397 uint64_t ns;
398 do
399 ns = RTTimeNanoTS();
400 while (ns == nsStart);
401 nsStart = ns;
402
403 /* Call it for 10 ms. */
404 uint32_t i = 0;
405 do
406 {
407 i++;
408 ns = RTTimeNanoTS();
409 }
410 while (ns - nsStart < RT_NS_10MS);
411
412 g_nsPerNanoTSCall = (ns - nsStart) / i;
413}
414
415
416/**
417 * Construct a path relative to the base test directory.
418 *
419 * @returns g_szDir.
420 * @param pszAppend What to append.
421 * @param cchAppend How much to append.
422 */
423DECLINLINE(char *) InDir(const char *pszAppend, size_t cchAppend)
424{
425 Assert(g_szDir[g_cchDir - 1] == RTPATH_SLASH);
426 memcpy(&g_szDir[g_cchDir], pszAppend, cchAppend);
427 g_szDir[g_cchDir + cchAppend] = '\0';
428 return &g_szDir[0];
429}
430
431
432/**
433 * Construct a path relative to the empty directory.
434 *
435 * @returns g_szEmptyDir.
436 * @param pszAppend What to append.
437 * @param cchAppend How much to append.
438 */
439DECLINLINE(char *) InEmptyDir(const char *pszAppend, size_t cchAppend)
440{
441 Assert(g_szEmptyDir[g_cchEmptyDir - 1] == RTPATH_SLASH);
442 memcpy(&g_szEmptyDir[g_cchEmptyDir], pszAppend, cchAppend);
443 g_szEmptyDir[g_cchEmptyDir + cchAppend] = '\0';
444 return &g_szEmptyDir[0];
445}
446
447
448/**
449 * Construct a path relative to the deep test directory.
450 *
451 * @returns g_szDeepDir.
452 * @param pszAppend What to append.
453 * @param cchAppend How much to append.
454 */
455DECLINLINE(char *) InDeepDir(const char *pszAppend, size_t cchAppend)
456{
457 Assert(g_szDeepDir[g_cchDeepDir - 1] == RTPATH_SLASH);
458 memcpy(&g_szDeepDir[g_cchDeepDir], pszAppend, cchAppend);
459 g_szDeepDir[g_cchDeepDir + cchAppend] = '\0';
460 return &g_szDeepDir[0];
461}
462
463
464/**
465 * Prepares the test area.
466 * @returns VBox status code.
467 */
468static int fsPrepTestArea(void)
469{
470 /* The empty subdir and associated globals: */
471 static char s_szEmpty[] = "empty";
472 memcpy(g_szEmptyDir, g_szDir, g_cchDir);
473 memcpy(&g_szEmptyDir[g_cchDir], s_szEmpty, sizeof(s_szEmpty));
474 g_cchEmptyDir = g_cchDir + sizeof(s_szEmpty) - 1;
475 RTTESTI_CHECK_RC_RET(RTDirCreate(g_szEmptyDir, 0755, 0), VINF_SUCCESS, rcCheck);
476 g_szEmptyDir[g_cchEmptyDir++] = RTPATH_SLASH;
477 g_szEmptyDir[g_cchEmptyDir] = '\0';
478 RTTestIPrintf(RTTESTLVL_ALWAYS, "Empty dir: %s\n", g_szEmptyDir);
479
480 /* Deep directory: */
481 memcpy(g_szDeepDir, g_szDir, g_cchDir);
482 g_cchDeepDir = g_cchDir;
483 do
484 {
485 static char const s_szSub[] = "d" RTPATH_SLASH_STR;
486 memcpy(&g_szDeepDir[g_cchDeepDir], s_szSub, sizeof(s_szSub));
487 g_cchDeepDir += sizeof(s_szSub) - 1;
488 RTTESTI_CHECK_RC_RET( RTDirCreate(g_szDeepDir, 0755, 0), VINF_SUCCESS, rcCheck);
489 } while (g_cchDeepDir < 176);
490 RTTestIPrintf(RTTESTLVL_ALWAYS, "Deep dir: %s\n", g_szDeepDir);
491
492 /* Create known file in both deep and shallow dirs: */
493 RTFILE hKnownFile;
494 RTTESTI_CHECK_RC_RET(RTFileOpen(&hKnownFile, InDir(RT_STR_TUPLE("known-file")),
495 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE),
496 VINF_SUCCESS, rcCheck);
497 RTTESTI_CHECK_RC_RET(RTFileClose(hKnownFile), VINF_SUCCESS, rcCheck);
498
499 RTTESTI_CHECK_RC_RET(RTFileOpen(&hKnownFile, InDeepDir(RT_STR_TUPLE("known-file")),
500 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE),
501 VINF_SUCCESS, rcCheck);
502 RTTESTI_CHECK_RC_RET(RTFileClose(hKnownFile), VINF_SUCCESS, rcCheck);
503
504 return VINF_SUCCESS;
505}
506
507
508/**
509 * Create a name list entry.
510 * @returns Pointer to the entry, NULL if out of memory.
511 * @param pchName The name.
512 * @param cchName The name length.
513 */
514PFSPERFNAMEENTRY fsPerfCreateNameEntry(const char *pchName, size_t cchName)
515{
516 PFSPERFNAMEENTRY pEntry = (PFSPERFNAMEENTRY)RTMemAllocVar(RT_UOFFSETOF_DYN(FSPERFNAMEENTRY, szName[cchName + 1]));
517 if (pEntry)
518 {
519 RTListInit(&pEntry->Entry);
520 pEntry->cchName = (uint16_t)cchName;
521 memcpy(pEntry->szName, pchName, cchName);
522 pEntry->szName[cchName] = '\0';
523 }
524 return pEntry;
525}
526
527
528static int fsPerfManyTreeRecursiveDirCreator(size_t cchDir, uint32_t iDepth)
529{
530 PFSPERFNAMEENTRY pEntry = fsPerfCreateNameEntry(g_szDir, cchDir);
531 RTTESTI_CHECK_RET(pEntry, VERR_NO_MEMORY);
532 RTListAppend(&g_ManyTreeHead, &pEntry->Entry);
533
534 RTTESTI_CHECK_RC_RET(RTDirCreate(g_szDir, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL),
535 VINF_SUCCESS, rcCheck);
536
537 if (iDepth < g_cManyTreeDepth)
538 for (uint32_t i = 0; i < g_cManyTreeSubdirsPerDir; i++)
539 {
540 size_t cchSubDir = RTStrPrintf(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, "d%02u" RTPATH_SLASH_STR, i);
541 RTTESTI_CHECK_RC_RET(fsPerfManyTreeRecursiveDirCreator(cchDir + cchSubDir, iDepth + 1), VINF_SUCCESS, rcCheck);
542 }
543
544 return VINF_SUCCESS;
545}
546
547
548void fsPerfManyFiles(void)
549{
550 RTTestISub("manyfiles");
551
552 /*
553 * Create a sub-directory with like 10000 files in it.
554 *
555 * This does push the directory organization of the underlying file system,
556 * which is something we might not want to profile with shared folders. It
557 * is however useful for directory enumeration.
558 */
559 RTTESTI_CHECK_RC_RETV(RTDirCreate(InDir(RT_STR_TUPLE("manyfiles")), 0755,
560 RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL),
561 VINF_SUCCESS);
562
563 size_t offFilename = strlen(g_szDir);
564 g_szDir[offFilename++] = RTPATH_SLASH;
565
566 fsPerfYield();
567 RTFILE hFile;
568 uint64_t const nsStart = RTTimeNanoTS();
569 for (uint32_t i = 0; i < g_cManyFiles; i++)
570 {
571 RTStrFormatU32(&g_szDir[offFilename], sizeof(g_szDir) - offFilename, i, 10, 5, 5, RTSTR_F_ZEROPAD);
572 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile, g_szDir, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
573 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
574 }
575 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
576 RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Creating %u empty files in single directory", g_cManyFiles);
577 RTTestIValueF(cNsElapsed / g_cManyFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Create empty file (single dir)");
578
579 /*
580 * Create a bunch of directories with exacly 32 files in each, hoping to
581 * avoid any directory organization artifacts.
582 */
583 /* Create the directories first, building a list of them for simplifying iteration: */
584 RTListInit(&g_ManyTreeHead);
585 InDir(RT_STR_TUPLE("manytree" RTPATH_SLASH_STR));
586 RTTESTI_CHECK_RC_RETV(fsPerfManyTreeRecursiveDirCreator(strlen(g_szDir), 0), VINF_SUCCESS);
587
588 /* Create the zero byte files: */
589 fsPerfYield();
590 uint64_t const nsStart2 = RTTimeNanoTS();
591 uint32_t cFiles = 0;
592 PFSPERFNAMEENTRY pCur;
593 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry)
594 {
595 char szPath[RTPATH_MAX];
596 memcpy(szPath, pCur->szName, pCur->cchName);
597 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++)
598 {
599 RTStrFormatU32(&szPath[pCur->cchName], sizeof(szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD);
600 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile, szPath, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
601 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
602 cFiles++;
603 }
604 }
605 uint64_t const cNsElapsed2 = RTTimeNanoTS() - nsStart2;
606 RTTestIValueF(cNsElapsed2, RTTESTUNIT_NS, "Creating %u empty files in tree", cFiles);
607 RTTestIValueF(cNsElapsed2 / cFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Create empty file (tree)");
608 RTTESTI_CHECK(g_cManyTreeFiles == cFiles);
609}
610
611
612DECL_FORCE_INLINE(int) fsPerfOpenExistingOnceReadonly(const char *pszFile)
613{
614 RTFILE hFile;
615 RTTESTI_CHECK_RC_RET(RTFileOpen(&hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS, rcCheck);
616 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
617 return VINF_SUCCESS;
618}
619
620
621DECL_FORCE_INLINE(int) fsPerfOpenExistingOnceWriteonly(const char *pszFile)
622{
623 RTFILE hFile;
624 RTTESTI_CHECK_RC_RET(RTFileOpen(&hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS, rcCheck);
625 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
626 return VINF_SUCCESS;
627}
628
629
630void fsPerfOpen(void)
631{
632 RTTestISub("open");
633
634 /* Opening non-existing files. */
635 RTFILE hFile;
636 RTTESTI_CHECK_RC(RTFileOpen(&hFile, InEmptyDir(RT_STR_TUPLE("no-such-file")),
637 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VERR_FILE_NOT_FOUND);
638 RTTESTI_CHECK_RC(RTFileOpen(&hFile, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
639 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), FSPERF_VERR_PATH_NOT_FOUND);
640 RTTESTI_CHECK_RC(RTFileOpen(&hFile, InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
641 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VERR_PATH_NOT_FOUND);
642
643 /*
644 * Create file1 and then try exclusivly creating it again.
645 * Then profile opening it for reading.
646 */
647 RTFILE hFile1;
648 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file1")),
649 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
650 RTTESTI_CHECK_RC(RTFileOpen(&hFile, g_szDir, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VERR_ALREADY_EXISTS);
651 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
652
653 PROFILE_FN(fsPerfOpenExistingOnceReadonly(g_szDir), g_nsTestRun, "RTFileOpen/Close/Readonly");
654 PROFILE_FN(fsPerfOpenExistingOnceWriteonly(g_szDir), g_nsTestRun, "RTFileOpen/Close/Writeonly");
655
656 /*
657 * Profile opening in the deep directory too.
658 */
659 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file1")),
660 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
661 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
662 PROFILE_FN(fsPerfOpenExistingOnceReadonly(g_szDeepDir), g_nsTestRun, "RTFileOpen/Close/deep/readonly");
663 PROFILE_FN(fsPerfOpenExistingOnceWriteonly(g_szDeepDir), g_nsTestRun, "RTFileOpen/Close/deep/writeonly");
664
665 /* Manytree: */
666 char szPath[RTPATH_MAX];
667 PROFILE_MANYTREE_FN(szPath, fsPerfOpenExistingOnceReadonly(szPath), 1, g_nsTestRun, "RTFileOpen/Close/manytree/readonly");
668}
669
670
671void fsPerfFStat(void)
672{
673 RTTestISub("fstat");
674 RTFILE hFile1;
675 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file2")),
676 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
677 RTFSOBJINFO ObjInfo = {0};
678 PROFILE_FN(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_NOTHING), g_nsTestRun, "RTFileQueryInfo/NOTHING");
679 PROFILE_FN(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_UNIX), g_nsTestRun, "RTFileQueryInfo/UNIX");
680
681 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
682}
683
684
685void fsPerfFChMod(void)
686{
687 RTTestISub("fchmod");
688 RTFILE hFile1;
689 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file4")),
690 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
691 RTFSOBJINFO ObjInfo = {0};
692 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
693 RTFMODE const fEvenMode = (ObjInfo.Attr.fMode & ~RTFS_UNIX_ALL_ACCESS_PERMS) | RTFS_DOS_READONLY | 0400;
694 RTFMODE const fOddMode = (ObjInfo.Attr.fMode & ~(RTFS_UNIX_ALL_ACCESS_PERMS | RTFS_DOS_READONLY)) | 0640;
695 PROFILE_FN(RTFileSetMode(hFile1, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTFileSetMode");
696
697 RTFileSetMode(hFile1, ObjInfo.Attr.fMode);
698 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
699}
700
701
702void fsPerfFUtimes(void)
703{
704 RTTestISub("futimes");
705 RTFILE hFile1;
706 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file5")),
707 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
708 RTTIMESPEC Time1;
709 RTTimeNow(&Time1);
710 RTTIMESPEC Time2 = Time1;
711 RTTimeSpecSubSeconds(&Time2, 3636);
712
713 RTFSOBJINFO ObjInfo0 = {0};
714 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo0, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
715
716 /* Modify modification time: */
717 RTTESTI_CHECK_RC(RTFileSetTimes(hFile1, NULL, &Time2, NULL, NULL), VINF_SUCCESS);
718 RTFSOBJINFO ObjInfo1 = {0};
719 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo1, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
720 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo1.ModificationTime) >> 2) == (RTTimeSpecGetSeconds(&Time2) >> 2));
721 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo1.AccessTime) == RTTimeSpecGetNano(&ObjInfo0.AccessTime));
722
723 /* Modify access time: */
724 RTTESTI_CHECK_RC(RTFileSetTimes(hFile1, &Time1, NULL, NULL, NULL), VINF_SUCCESS);
725 RTFSOBJINFO ObjInfo2 = {0};
726 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo2, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
727 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo2.AccessTime) >> 2) == (RTTimeSpecGetSeconds(&Time1) >> 2));
728 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo2.ModificationTime) == RTTimeSpecGetNano(&ObjInfo1.ModificationTime));
729
730 /* Benchmark it: */
731 PROFILE_FN(RTFileSetTimes(hFile1, NULL, iIteration & 1 ? &Time1 : &Time2, NULL, NULL), g_nsTestRun, "RTFileSetTimes");
732
733 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
734}
735
736
737void fsPerfStat(void)
738{
739 RTTestISub("stat");
740 RTFSOBJINFO ObjInfo;
741
742 /* Non-existing files. */
743 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InEmptyDir(RT_STR_TUPLE("no-such-file")),
744 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VERR_FILE_NOT_FOUND);
745 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
746 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), FSPERF_VERR_PATH_NOT_FOUND);
747 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
748 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VERR_PATH_NOT_FOUND);
749
750 /* Shallow: */
751 RTFILE hFile1;
752 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file3")),
753 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
754 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
755
756 PROFILE_FN(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), g_nsTestRun,
757 "RTPathQueryInfoEx/NOTHING");
758 PROFILE_FN(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK), g_nsTestRun,
759 "RTPathQueryInfoEx/UNIX");
760
761
762 /* Deep: */
763 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file3")),
764 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
765 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
766
767 PROFILE_FN(RTPathQueryInfoEx(g_szDeepDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), g_nsTestRun,
768 "RTPathQueryInfoEx/deep/NOTHING");
769 PROFILE_FN(RTPathQueryInfoEx(g_szDeepDir, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK), g_nsTestRun,
770 "RTPathQueryInfoEx/deep/UNIX");
771
772 /* Manytree: */
773 char szPath[RTPATH_MAX];
774 PROFILE_MANYTREE_FN(szPath, RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK),
775 1, g_nsTestRun, "RTPathQueryInfoEx/manytree/NOTHING");
776 PROFILE_MANYTREE_FN(szPath, RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK),
777 1, g_nsTestRun, "RTPathQueryInfoEx/manytree/UNIX");
778}
779
780
781void fsPerfChmod(void)
782{
783 RTTestISub("chmod");
784
785 /* Non-existing files. */
786 RTTESTI_CHECK_RC(RTPathSetMode(InEmptyDir(RT_STR_TUPLE("no-such-file")), 0665),
787 VERR_FILE_NOT_FOUND);
788 RTTESTI_CHECK_RC(RTPathSetMode(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), 0665),
789 FSPERF_VERR_PATH_NOT_FOUND);
790 RTTESTI_CHECK_RC(RTPathSetMode(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), 0665), VERR_PATH_NOT_FOUND);
791
792 /* Shallow: */
793 RTFILE hFile1;
794 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file14")),
795 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
796 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
797
798 RTFSOBJINFO ObjInfo;
799 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
800 RTFMODE const fEvenMode = (ObjInfo.Attr.fMode & ~RTFS_UNIX_ALL_ACCESS_PERMS) | RTFS_DOS_READONLY | 0400;
801 RTFMODE const fOddMode = (ObjInfo.Attr.fMode & ~(RTFS_UNIX_ALL_ACCESS_PERMS | RTFS_DOS_READONLY)) | 0640;
802 PROFILE_FN(RTPathSetMode(g_szDir, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTPathSetMode");
803 RTPathSetMode(g_szDir, ObjInfo.Attr.fMode);
804
805 /* Deep: */
806 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file14")),
807 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
808 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
809
810 PROFILE_FN(RTPathSetMode(g_szDeepDir, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTPathSetMode/deep");
811 RTPathSetMode(g_szDeepDir, ObjInfo.Attr.fMode);
812
813 /* Manytree: */
814 char szPath[RTPATH_MAX];
815 PROFILE_MANYTREE_FN(szPath, RTPathSetMode(szPath, iIteration & 1 ? fOddMode : fEvenMode), 1, g_nsTestRun,
816 "RTPathSetMode/manytree");
817 DO_MANYTREE_FN(szPath, RTPathSetMode(szPath, ObjInfo.Attr.fMode));
818}
819
820
821void fsPerfUtimes(void)
822{
823 RTTestISub("utimes");
824
825 RTTIMESPEC Time1;
826 RTTimeNow(&Time1);
827 RTTIMESPEC Time2 = Time1;
828 RTTimeSpecSubSeconds(&Time2, 3636);
829
830 /* Non-existing files. */
831 RTTESTI_CHECK_RC(RTPathSetTimesEx(InEmptyDir(RT_STR_TUPLE("no-such-file")), NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
832 VERR_FILE_NOT_FOUND);
833 RTTESTI_CHECK_RC(RTPathSetTimesEx(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
834 NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
835 FSPERF_VERR_PATH_NOT_FOUND);
836 RTTESTI_CHECK_RC(RTPathSetTimesEx(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
837 NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
838 VERR_PATH_NOT_FOUND);
839
840 /* Shallow: */
841 RTFILE hFile1;
842 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file15")),
843 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
844 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
845
846 RTFSOBJINFO ObjInfo0 = {0};
847 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo0, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
848
849 /* Modify modification time: */
850 RTTESTI_CHECK_RC(RTPathSetTimesEx(g_szDir, NULL, &Time2, NULL, NULL, RTPATH_F_ON_LINK), VINF_SUCCESS);
851 RTFSOBJINFO ObjInfo1;
852 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo1, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
853 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo1.ModificationTime) >> 2) == (RTTimeSpecGetSeconds(&Time2) >> 2));
854 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo1.AccessTime) == RTTimeSpecGetNano(&ObjInfo0.AccessTime));
855
856 /* Modify access time: */
857 RTTESTI_CHECK_RC(RTPathSetTimesEx(g_szDir, &Time1, NULL, NULL, NULL, RTPATH_F_ON_LINK), VINF_SUCCESS);
858 RTFSOBJINFO ObjInfo2 = {0};
859 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo2, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
860 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo2.AccessTime) >> 2) == (RTTimeSpecGetSeconds(&Time1) >> 2));
861 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo2.ModificationTime) == RTTimeSpecGetNano(&ObjInfo1.ModificationTime));
862
863 /* Profile shallow: */
864 PROFILE_FN(RTPathSetTimesEx(g_szDir, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
865 NULL, NULL, RTPATH_F_ON_LINK),
866 g_nsTestRun, "RTPathSetTimesEx");
867
868 /* Deep: */
869 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file15")),
870 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
871 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
872
873 PROFILE_FN(RTPathSetTimesEx(g_szDeepDir, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
874 NULL, NULL, RTPATH_F_ON_LINK),
875 g_nsTestRun, "RTPathSetTimesEx/deep");
876
877 /* Manytree: */
878 char szPath[RTPATH_MAX];
879 PROFILE_MANYTREE_FN(szPath, RTPathSetTimesEx(szPath, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
880 NULL, NULL, RTPATH_F_ON_LINK),
881 1, g_nsTestRun, "RTPathSetTimesEx/manytree");
882}
883
884
885DECL_FORCE_INLINE(int) fsPerfRenameMany(const char *pszFile, uint32_t iIteration)
886{
887 char szRenamed[RTPATH_MAX];
888 strcat(strcpy(szRenamed, pszFile), "-renamed");
889 if (!(iIteration & 1))
890 return RTPathRename(pszFile, szRenamed, 0);
891 return RTPathRename(szRenamed, pszFile, 0);
892}
893
894
895void fsPerfRename(void)
896{
897 RTTestISub("rename");
898 char szPath[RTPATH_MAX];
899
900 /* Non-existing files. */
901 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("other-no-such-file")));
902 RTTESTI_CHECK_RC(RTPathRename(InEmptyDir(RT_STR_TUPLE("no-such-file")), szPath, 0), VERR_FILE_NOT_FOUND);
903 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "other-no-such-file")));
904 RTTESTI_CHECK_RC(RTPathRename(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), szPath, 0),
905 FSPERF_VERR_PATH_NOT_FOUND);
906 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("other-no-such-file")));
907 RTTESTI_CHECK_RC(RTPathRename(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), szPath, 0), VERR_PATH_NOT_FOUND);
908
909 RTFILE hFile1;
910 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file16")),
911 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
912 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
913 strcat(strcpy(szPath, g_szDir), "-no-such-dir" RTPATH_SLASH_STR "file16");
914 RTTESTI_CHECK_RC(RTPathRename(szPath, g_szDir, 0), FSPERF_VERR_PATH_NOT_FOUND);
915 RTTESTI_CHECK_RC(RTPathRename(g_szDir, szPath, 0), FSPERF_VERR_PATH_NOT_FOUND);
916
917 /* Shallow: */
918 strcat(strcpy(szPath, g_szDir), "-other");
919 PROFILE_FN(RTPathRename(iIteration & 1 ? szPath : g_szDir, iIteration & 1 ? g_szDir : szPath, 0), g_nsTestRun, "RTPathRename");
920
921 /* Deep: */
922 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file15")),
923 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
924 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
925
926 strcat(strcpy(szPath, g_szDeepDir), "-other");
927 PROFILE_FN(RTPathRename(iIteration & 1 ? szPath : g_szDeepDir, iIteration & 1 ? g_szDeepDir : szPath, 0),
928 g_nsTestRun, "RTPathRename/deep");
929
930 /* Manytree: */
931 PROFILE_MANYTREE_FN(szPath, fsPerfRenameMany(szPath, iIteration), 2, g_nsTestRun, "RTPathRename/manytree");
932}
933
934
935DECL_FORCE_INLINE(int) fsPerfEnumEmpty(void)
936{
937 RTDIR hDir;
938 g_szEmptyDir[g_cchEmptyDir] = '\0';
939 RTTESTI_CHECK_RC_RET(RTDirOpen(&hDir, g_szEmptyDir), VINF_SUCCESS, rcCheck);
940
941 RTDIRENTRY Entry;
942 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
943 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
944 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
945
946 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
947 return VINF_SUCCESS;
948}
949
950
951DECL_FORCE_INLINE(int) fsPerfEnumManyFiles(void)
952{
953 RTDIR hDir;
954 RTTESTI_CHECK_RC_RET(RTDirOpen(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS, rcCheck);
955 uint32_t cLeft = g_cManyFiles + 2;
956 for (;;)
957 {
958 RTDIRENTRY Entry;
959 if (cLeft > 0)
960 RTTESTI_CHECK_RC_BREAK(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
961 else
962 {
963 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
964 break;
965 }
966 cLeft--;
967 }
968 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
969 return VINF_SUCCESS;
970}
971
972
973void vsPerfDirEnum(void)
974{
975 RTTestISub("dir enum");
976 RTDIR hDir;
977
978 /* Non-existing files. */
979 RTTESTI_CHECK_RC(RTDirOpen(&hDir, InEmptyDir(RT_STR_TUPLE("no-such-file"))), VERR_FILE_NOT_FOUND);
980 RTTESTI_CHECK_RC(RTDirOpen(&hDir, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
981 RTTESTI_CHECK_RC(RTDirOpen(&hDir, InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
982
983 /*
984 * The empty directory.
985 */
986 g_szEmptyDir[g_cchEmptyDir] = '\0';
987 RTTESTI_CHECK_RC_RETV(RTDirOpen(&hDir, g_szEmptyDir), VINF_SUCCESS);
988
989 uint32_t fDots = 0;
990 RTDIRENTRY Entry;
991 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
992 RTTESTI_CHECK(RTDirEntryIsStdDotLink(&Entry));
993 fDots |= RT_BIT_32(Entry.cbName - 1);
994
995 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
996 RTTESTI_CHECK(RTDirEntryIsStdDotLink(&Entry));
997 fDots |= RT_BIT_32(Entry.cbName - 1);
998 RTTESTI_CHECK(fDots == 3);
999
1000 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
1001
1002 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
1003
1004 /*
1005 * The directory with many files in it.
1006 */
1007 if (g_fManyFiles)
1008 {
1009 fDots = 0;
1010 uint32_t const cBitmap = RT_ALIGN_32(g_cManyFiles, 64);
1011 void *pvBitmap = alloca(cBitmap / 8);
1012 RT_BZERO(pvBitmap, cBitmap / 8);
1013 for (uint32_t i = g_cManyFiles; i < cBitmap; i++)
1014 ASMBitSet(pvBitmap, i);
1015
1016 uint32_t cFiles = 0;
1017 RTTESTI_CHECK_RC_RETV(RTDirOpen(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS);
1018 for (;;)
1019 {
1020 int rc = RTDirRead(hDir, &Entry, NULL);
1021 if (rc == VINF_SUCCESS)
1022 {
1023 if (Entry.szName[0] == '.')
1024 {
1025 if (Entry.szName[1] == '.')
1026 {
1027 RTTESTI_CHECK(!(fDots & 2));
1028 fDots |= 2;
1029 }
1030 else
1031 {
1032 RTTESTI_CHECK(Entry.szName[1] == '\0');
1033 RTTESTI_CHECK(!(fDots & 1));
1034 fDots |= 1;
1035 }
1036 }
1037 else
1038 {
1039 uint32_t iFile = UINT32_MAX;
1040 RTTESTI_CHECK_RC(RTStrToUInt32Full(Entry.szName, 10, &iFile), VINF_SUCCESS);
1041 if ( iFile < g_cManyFiles
1042 && !ASMBitTest(pvBitmap, iFile))
1043 {
1044 ASMBitSet(pvBitmap, iFile);
1045 cFiles++;
1046 }
1047 else
1048 RTTestFailed(g_hTest, "line %u: iFile=%u g_cManyFiles=%u\n", __LINE__, iFile, g_cManyFiles);
1049 }
1050 }
1051 else if (rc == VERR_NO_MORE_FILES)
1052 break;
1053 else
1054 {
1055 RTTestFailed(g_hTest, "RTDirRead failed enumerating manyfiles: %Rrc\n", rc);
1056 RTDirClose(hDir);
1057 return;
1058 }
1059 }
1060 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
1061 RTTESTI_CHECK(fDots == 3);
1062 RTTESTI_CHECK(cFiles == g_cManyFiles);
1063 RTTESTI_CHECK(ASMMemIsAllU8(pvBitmap, cBitmap / 8, 0xff));
1064 }
1065
1066 /*
1067 * Profile.
1068 */
1069 PROFILE_FN(fsPerfEnumEmpty(),g_nsTestRun, "RTDirOpen/Read/Close empty");
1070 if (g_fManyFiles)
1071 PROFILE_FN(fsPerfEnumManyFiles(), g_nsTestRun, "RTDirOpen/Read/Close manyfiles");
1072}
1073
1074
1075void fsPerfMkRmDir(void)
1076{
1077 RTTestISub("mkdir/rmdir");
1078
1079 /* Non-existing directories: */
1080 RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir"))), VERR_FILE_NOT_FOUND);
1081 RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
1082 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
1083 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), 0755, 0), FSPERF_VERR_PATH_NOT_FOUND);
1084 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), 0755, 0), VERR_PATH_NOT_FOUND);
1085
1086 /** @todo check what happens if non-final path component isn't a directory. unix
1087 * should return ENOTDIR and IPRT translates that to VERR_PATH_NOT_FOUND.
1088 * Curious what happens on windows. */
1089
1090 /* Already existing directories and files: */
1091 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE(".")), 0755, 0), VERR_ALREADY_EXISTS);
1092 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE("..")), 0755, 0), VERR_ALREADY_EXISTS);
1093
1094 /* Remove directory with subdirectories: */
1095#if defined(RT_OS_WINDOWS)
1096 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("."))), VERR_DIR_NOT_EMPTY);
1097#else
1098 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("."))), VERR_INVALID_PARAMETER); /* EINVAL for '.' */
1099#endif
1100#if defined(RT_OS_WINDOWS)
1101 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(".."))), VERR_SHARING_VIOLATION); /* weird */
1102#else
1103 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(".."))), VERR_DIR_NOT_EMPTY);
1104#endif
1105 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(""))), VERR_DIR_NOT_EMPTY);
1106
1107 /* Create a directory and remove it: */
1108 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("subdir-1")), 0755, 0), VINF_SUCCESS);
1109 RTTESTI_CHECK_RC(RTDirRemove(g_szDir), VINF_SUCCESS);
1110
1111 /* Create a file and try remove it or create a directory with the same name: */
1112 RTFILE hFile1;
1113 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file18")),
1114 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
1115 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
1116 RTTESTI_CHECK_RC(RTDirRemove(g_szDir), VERR_NOT_A_DIRECTORY);
1117 RTTESTI_CHECK_RC(RTDirCreate(g_szDir, 0755, 0), VERR_ALREADY_EXISTS);
1118 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("file18" RTPATH_SLASH_STR "subdir")), 0755, 0), VERR_PATH_NOT_FOUND);
1119
1120 /*
1121 * Profile alternately creating and removing a bunch of directories.
1122 */
1123 RTTESTI_CHECK_RC_RETV(RTDirCreate(InDir(RT_STR_TUPLE("subdir-2")), 0755, 0), VINF_SUCCESS);
1124 size_t cchDir = strlen(g_szDir);
1125 g_szDir[cchDir++] = RTPATH_SLASH;
1126 g_szDir[cchDir++] = 's';
1127
1128 uint32_t cCreated = 0;
1129 uint64_t nsCreate = 0;
1130 uint64_t nsRemove = 0;
1131 for (;;)
1132 {
1133 /* Create a bunch: */
1134 uint64_t nsStart = RTTimeNanoTS();
1135 for (uint32_t i = 0; i < 998; i++)
1136 {
1137 RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD);
1138 RTTESTI_CHECK_RC_RETV(RTDirCreate(g_szDir, 0755, 0), VINF_SUCCESS);
1139 }
1140 nsCreate += RTTimeNanoTS() - nsStart;
1141 cCreated += 998;
1142
1143 /* Remove the bunch: */
1144 nsStart = RTTimeNanoTS();
1145 for (uint32_t i = 0; i < 998; i++)
1146 {
1147 RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD);
1148 RTTESTI_CHECK_RC_RETV(RTDirRemove(g_szDir), VINF_SUCCESS);
1149 }
1150 nsRemove = RTTimeNanoTS() - nsStart;
1151
1152 /* Check if we got time for another round: */
1153 if ( ( nsRemove >= g_nsTestRun
1154 && nsCreate >= g_nsTestRun)
1155 || nsCreate + nsRemove >= g_nsTestRun * 3)
1156 break;
1157 }
1158 RTTestIValue("RTDirCreate", nsCreate / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE);
1159 RTTestIValue("RTDirRemove", nsRemove / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE);
1160}
1161
1162
1163void fsPerfStatVfs(void)
1164{
1165 RTTestISub("statvfs");
1166
1167 g_szEmptyDir[g_cchEmptyDir] = '\0';
1168 RTFOFF cbTotal;
1169 RTFOFF cbFree;
1170 uint32_t cbBlock;
1171 uint32_t cbSector;
1172 RTTESTI_CHECK_RC(RTFsQuerySizes(g_szEmptyDir, &cbTotal, &cbFree, &cbBlock, &cbSector), VINF_SUCCESS);
1173
1174 uint32_t uSerial;
1175 RTTESTI_CHECK_RC(RTFsQuerySerial(g_szEmptyDir, &uSerial), VINF_SUCCESS);
1176
1177 RTFSPROPERTIES Props;
1178 RTTESTI_CHECK_RC(RTFsQueryProperties(g_szEmptyDir, &Props), VINF_SUCCESS);
1179
1180 RTFSTYPE enmType;
1181 RTTESTI_CHECK_RC(RTFsQueryType(g_szEmptyDir, &enmType), VINF_SUCCESS);
1182
1183}
1184
1185
1186void fsPerfRm(void)
1187{
1188 RTTestISub("rm");
1189
1190 /* Non-existing files. */
1191 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-file"))), VERR_FILE_NOT_FOUND);
1192 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
1193 RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
1194
1195 /* Directories: */
1196 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("."))), VERR_ACCESS_DENIED);
1197 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(".."))), VERR_ACCESS_DENIED);
1198 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(""))), VERR_ACCESS_DENIED);
1199
1200 /* Shallow: */
1201 RTFILE hFile1;
1202 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file19")),
1203 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
1204 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
1205 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
1206 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VERR_FILE_NOT_FOUND);
1207
1208 if (g_fManyFiles)
1209 {
1210 /*
1211 * Profile the deletion of the manyfiles content.
1212 */
1213 {
1214 InDir(RT_STR_TUPLE("manyfiles" RTPATH_SLASH_STR));
1215 size_t const offFilename = strlen(g_szDir);
1216 fsPerfYield();
1217 uint64_t const nsStart = RTTimeNanoTS();
1218 for (uint32_t i = 0; i < g_cManyFiles; i++)
1219 {
1220 RTStrFormatU32(&g_szDir[offFilename], sizeof(g_szDir) - offFilename, i, 10, 5, 5, RTSTR_F_ZEROPAD);
1221 RTTESTI_CHECK_RC_RETV(RTFileDelete(g_szDir), VINF_SUCCESS);
1222 }
1223 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
1224 RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Deleted %u empty files from a single directory", g_cManyFiles);
1225 RTTestIValueF(cNsElapsed / g_cManyFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Delete file (single dir)");
1226 }
1227
1228 /*
1229 * Ditto for the manytree.
1230 */
1231 {
1232 char szPath[RTPATH_MAX];
1233 uint64_t const nsStart = RTTimeNanoTS();
1234 DO_MANYTREE_FN(szPath, RTTESTI_CHECK_RC_RETV(RTFileDelete(szPath), VINF_SUCCESS));
1235 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
1236 RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Deleted %u empty files in tree", g_cManyTreeFiles);
1237 RTTestIValueF(cNsElapsed / g_cManyTreeFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Delete file (tree)");
1238 }
1239 }
1240}
1241
1242
1243void fsPerfChSize(void)
1244{
1245 RTTestISub("chsize");
1246
1247 /*
1248 * We need some free space to perform this test.
1249 */
1250 g_szDir[g_cchDir] = '\0';
1251 RTFOFF cbFree = 0;
1252 RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
1253 if (cbFree < _1M)
1254 {
1255 RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 1MB", cbFree);
1256 return;
1257 }
1258
1259 /*
1260 * Create a file and play around with it's size.
1261 * We let the current file position follow the end position as we make changes.
1262 */
1263 RTFILE hFile1;
1264 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file20")),
1265 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
1266 uint64_t cbFile = UINT64_MAX;
1267 RTTESTI_CHECK_RC(RTFileGetSize(hFile1, &cbFile), VINF_SUCCESS);
1268 RTTESTI_CHECK(cbFile == 0);
1269
1270 uint8_t abBuf[4096];
1271 static uint64_t const s_acbChanges[] =
1272 {
1273 1023, 1024, 1024, 1025, 8192, 11111, _1M, _8M, _8M,
1274 _4M, _2M + 1, _1M - 1, 65537, 65536, 32768, 8000, 7999, 7998, 1024, 1, 0
1275 };
1276 uint64_t cbOld = 0;
1277 for (unsigned i = 0; i < RT_ELEMENTS(s_acbChanges); i++)
1278 {
1279 uint64_t cbNew = s_acbChanges[i];
1280 if (cbNew + _64K >= (uint64_t)cbFree)
1281 continue;
1282
1283 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, cbNew), VINF_SUCCESS);
1284 RTTESTI_CHECK_RC(RTFileGetSize(hFile1, &cbFile), VINF_SUCCESS);
1285 RTTESTI_CHECK_MSG(cbFile == cbNew, ("cbFile=%#RX64 cbNew=%#RX64\n", cbFile, cbNew));
1286
1287 if (cbNew > cbOld)
1288 {
1289 /* Check that the extension is all zeroed: */
1290 uint64_t cbLeft = cbNew - cbOld;
1291 while (cbLeft > 0)
1292 {
1293 memset(abBuf, 0xff, sizeof(abBuf));
1294 size_t cbToRead = sizeof(abBuf);
1295 if (cbToRead > cbLeft)
1296 cbToRead = (size_t)cbLeft;
1297 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, cbToRead, NULL), VINF_SUCCESS);
1298 RTTESTI_CHECK(ASMMemIsZero(abBuf, cbToRead));
1299 cbLeft -= cbToRead;
1300 }
1301 }
1302 else
1303 {
1304 /* Check that reading fails with EOF because current position is now beyond the end: */
1305 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 1, NULL), VERR_EOF);
1306
1307 /* Keep current position at the end of the file: */
1308 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbNew, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
1309 }
1310 cbOld = cbNew;
1311 }
1312
1313 /*
1314 * Profile just the file setting operation itself, keeping the changes within
1315 * an allocation unit to avoid needing to adjust the actual (host) FS allocation.
1316 * ASSUMES allocation unit >= 512 and power of two.
1317 */
1318 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, _64K), VINF_SUCCESS);
1319 PROFILE_FN(RTFileSetSize(hFile1, _64K - (iIteration & 255) - 128), g_nsTestRun, "RTFileSetSize/noalloc");
1320
1321 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS);
1322 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
1323 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
1324}
1325
1326
1327int fsPerfIoPrepFile(RTFILE hFile1, uint64_t cbFile, uint8_t **ppbFree)
1328{
1329 /*
1330 * Seek to the end - 4K and write the last 4K.
1331 * This should have the effect of filling the whole file with zeros.
1332 */
1333 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, cbFile - _4K, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1334 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, g_abRTZero4K, _4K, NULL), VINF_SUCCESS, rcCheck);
1335
1336 /*
1337 * Check that the space we searched across actually is zero filled.
1338 */
1339 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1340 size_t cbBuf = _1M;
1341 uint8_t *pbBuf = *ppbFree = (uint8_t *)RTMemAlloc(cbBuf);
1342 RTTESTI_CHECK_RET(pbBuf != NULL, VERR_NO_MEMORY);
1343 uint64_t cbLeft = cbFile;
1344 while (cbLeft > 0)
1345 {
1346 size_t cbToRead = cbBuf;
1347 if (cbToRead > cbLeft)
1348 cbToRead = (size_t)cbLeft;
1349 pbBuf[cbToRead] = 0xff;
1350
1351 RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBuf, cbToRead, NULL), VINF_SUCCESS, rcCheck);
1352 RTTESTI_CHECK_RET(ASMMemIsZero(pbBuf, cbToRead), VERR_MISMATCH);
1353
1354 cbLeft -= cbToRead;
1355 }
1356
1357 /*
1358 * Fill the file with 0xf6 and insert offset markers with 1KB intervals.
1359 */
1360 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1361 memset(pbBuf, 0xf6, cbBuf);
1362 cbLeft = cbFile;
1363 uint64_t off = 0;
1364 while (cbLeft > 0)
1365 {
1366 Assert(!(off & (_1K - 1)));
1367 Assert(!(cbBuf & (_1K - 1)));
1368 for (size_t offBuf = 0; offBuf < cbBuf; offBuf += _1K, off += _1K)
1369 *(uint64_t *)&pbBuf[offBuf] = off;
1370
1371 size_t cbToWrite = cbBuf;
1372 if (cbToWrite > cbLeft)
1373 cbToWrite = (size_t)cbLeft;
1374
1375 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBuf, cbToWrite, NULL), VINF_SUCCESS, rcCheck);
1376
1377 cbLeft -= cbToWrite;
1378 }
1379
1380 return VINF_SUCCESS;
1381}
1382
1383
1384void fsPerfIoSeek(RTFILE hFile1, uint64_t cbFile)
1385{
1386 /*
1387 * Do a bunch of search tests, most which are random.
1388 */
1389 struct
1390 {
1391 int rc;
1392 uint32_t uMethod;
1393 int64_t offSeek;
1394 uint64_t offActual;
1395
1396 } aSeeks[9 + 64] =
1397 {
1398 { VINF_SUCCESS, RTFILE_SEEK_BEGIN, 0, 0 },
1399 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 },
1400 { VINF_SUCCESS, RTFILE_SEEK_END, 0, cbFile },
1401 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -4096, cbFile - 4096 },
1402 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 4096 - (int64_t)cbFile, 0 },
1403 { VINF_SUCCESS, RTFILE_SEEK_END, -(int64_t)cbFile/2, cbFile / 2 + (cbFile & 1) },
1404 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -(int64_t)cbFile/2, 0 },
1405#if defined(RT_OS_WINDOWS)
1406 { VERR_NEGATIVE_SEEK, RTFILE_SEEK_CURRENT, -1, 0 },
1407#else
1408 { VERR_INVALID_PARAMETER, RTFILE_SEEK_CURRENT, -1, 0 },
1409#endif
1410 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 },
1411 };
1412
1413 uint64_t offActual = 0;
1414 for (unsigned i = 9; i < RT_ELEMENTS(aSeeks); i++)
1415 {
1416 switch (RTRandU32Ex(RTFILE_SEEK_BEGIN, RTFILE_SEEK_END))
1417 {
1418 default: AssertFailedBreak();
1419 case RTFILE_SEEK_BEGIN:
1420 aSeeks[i].uMethod = RTFILE_SEEK_BEGIN;
1421 aSeeks[i].rc = VINF_SUCCESS;
1422 aSeeks[i].offSeek = RTRandU64Ex(0, cbFile + cbFile / 8);
1423 aSeeks[i].offActual = offActual = aSeeks[i].offSeek;
1424 break;
1425
1426 case RTFILE_SEEK_CURRENT:
1427 aSeeks[i].uMethod = RTFILE_SEEK_CURRENT;
1428 aSeeks[i].rc = VINF_SUCCESS;
1429 aSeeks[i].offSeek = (int64_t)RTRandU64Ex(0, cbFile + cbFile / 8) - (int64_t)offActual;
1430 aSeeks[i].offActual = offActual += aSeeks[i].offSeek;
1431 break;
1432
1433 case RTFILE_SEEK_END:
1434 aSeeks[i].uMethod = RTFILE_SEEK_END;
1435 aSeeks[i].rc = VINF_SUCCESS;
1436 aSeeks[i].offSeek = -(int64_t)RTRandU64Ex(0, cbFile);
1437 aSeeks[i].offActual = offActual = cbFile + aSeeks[i].offSeek;
1438 break;
1439 }
1440 }
1441
1442 for (unsigned iDoReadCheck = 0; iDoReadCheck < 2; iDoReadCheck++)
1443 {
1444 for (uint32_t i = 0; i < RT_ELEMENTS(aSeeks); i++)
1445 {
1446 offActual = UINT64_MAX;
1447 int rc = RTFileSeek(hFile1, aSeeks[i].offSeek, aSeeks[i].uMethod, &offActual);
1448 if (rc != aSeeks[i].rc)
1449 RTTestIFailed("Seek #%u: Expected %Rrc, got %Rrc", i, aSeeks[i].rc, rc);
1450 if (RT_SUCCESS(rc) && offActual != aSeeks[i].offActual)
1451 RTTestIFailed("Seek #%u: offActual %#RX64, expected %#RX64", i, offActual, aSeeks[i].offActual);
1452 if (RT_SUCCESS(rc))
1453 {
1454 uint64_t offTell = RTFileTell(hFile1);
1455 if (offTell != offActual)
1456 RTTestIFailed("Seek #%u: offActual %#RX64, RTFileTell %#RX64", i, offActual, offTell);
1457 }
1458
1459 if (RT_SUCCESS(rc) && offActual + _2K <= cbFile && iDoReadCheck)
1460 {
1461 uint8_t abBuf[_2K];
1462 RTTESTI_CHECK_RC(rc = RTFileRead(hFile1, abBuf, sizeof(abBuf), NULL), VINF_SUCCESS);
1463 if (RT_SUCCESS(rc))
1464 {
1465 size_t offMarker = (size_t)(RT_ALIGN_64(offActual, _1K) - offActual);
1466 uint64_t uMarker = *(uint64_t *)&abBuf[offMarker]; /** @todo potentially unaligned access */
1467 if (uMarker != offActual + offMarker)
1468 RTTestIFailed("Seek #%u: Invalid marker value (@ %#RX64): %#RX64, expected %#RX64",
1469 i, offActual, uMarker, offActual + offMarker);
1470
1471 RTTESTI_CHECK_RC(RTFileSeek(hFile1, -(int64_t)sizeof(abBuf), RTFILE_SEEK_CURRENT, NULL), VINF_SUCCESS);
1472 }
1473 }
1474 }
1475 }
1476
1477
1478 /*
1479 * Profile seeking relative to the beginning of the file and relative
1480 * to the end. The latter might be more expensive in a SF context.
1481 */
1482 PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? iIteration : iIteration % cbFile, RTFILE_SEEK_BEGIN, NULL),
1483 g_nsTestRun, "RTFileSeek/BEGIN");
1484 PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? -(int64_t)iIteration : -(int64_t)(iIteration % cbFile), RTFILE_SEEK_END, NULL),
1485 g_nsTestRun, "RTFileSeek/END");
1486
1487}
1488
1489
1490/** For fsPerfIoRead and fsPerfIoWrite. */
1491#define PROFILE_IO_FN(a_szOperation, a_fnCall) \
1492 do \
1493 { \
1494 RTTESTI_CHECK_RC_RETV(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS); \
1495 uint64_t offActual = 0; \
1496 uint32_t cSeeks = 0; \
1497 \
1498 /* Estimate how many iterations we need to fill up the given timeslot: */ \
1499 fsPerfYield(); \
1500 uint64_t nsStart = RTTimeNanoTS(); \
1501 uint64_t ns; \
1502 do \
1503 ns = RTTimeNanoTS(); \
1504 while (ns == nsStart); \
1505 nsStart = ns; \
1506 \
1507 uint64_t iIteration = 0; \
1508 do \
1509 { \
1510 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
1511 iIteration++; \
1512 ns = RTTimeNanoTS() - nsStart; \
1513 } while (ns < RT_NS_10MS); \
1514 ns /= iIteration; \
1515 if (ns > g_nsPerNanoTSCall + 32) \
1516 ns -= g_nsPerNanoTSCall; \
1517 uint64_t cIterations = g_nsTestRun / ns; \
1518 \
1519 /* Do the actual profiling: */ \
1520 cSeeks = 0; \
1521 iIteration = 0; \
1522 fsPerfYield(); \
1523 nsStart = RTTimeNanoTS(); \
1524 for (uint32_t iAdjust = 0; iAdjust < 4; iAdjust++) \
1525 { \
1526 for (; iIteration < cIterations; iIteration++)\
1527 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
1528 ns = RTTimeNanoTS() - nsStart;\
1529 if (ns >= g_nsTestRun - (g_nsTestRun / 10)) \
1530 break; \
1531 cIterations += cIterations / 4; \
1532 if (cIterations & 1) \
1533 cIterations++; \
1534 nsStart += g_nsPerNanoTSCall; \
1535 } \
1536 RTTestIValueF(ns / iIteration, \
1537 RTTESTUNIT_NS_PER_OCCURRENCE, a_szOperation "/seq/%RU32 latency", cbBlock); \
1538 RTTestIValueF((uint64_t)iIteration * cbBlock / ((double)ns / RT_NS_1SEC), \
1539 RTTESTUNIT_BYTES_PER_SEC, a_szOperation "/seq/%RU32 throughput", cbBlock); \
1540 RTTestIValueF(iIteration, \
1541 RTTESTUNIT_CALLS, a_szOperation "/seq/%RU32 calls", cbBlock); \
1542 RTTestIValueF((uint64_t)iIteration * cbBlock, \
1543 RTTESTUNIT_BYTES, a_szOperation "/seq/%RU32 bytes", cbBlock); \
1544 RTTestIValueF(cSeeks, \
1545 RTTESTUNIT_OCCURRENCES, a_szOperation "/seq/%RU32 seeks", cbBlock); \
1546 if (g_fShowDuration) \
1547 RTTestIValueF(ns, RTTESTUNIT_NS, a_szOperation "/seq/%RU32 duration", cbBlock); \
1548 } while (0)
1549
1550
1551DECL_FORCE_INLINE(int) fsPerfIoReadWorker(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t *pbBlock,
1552 uint64_t *poffActual, uint32_t *pcSeeks)
1553{
1554 /* Do we need to seek back to the start? */
1555 if (*poffActual + cbBlock <= cbFile)
1556 { /* likely */ }
1557 else
1558 {
1559 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1560 *pcSeeks += 1;
1561 *poffActual = 0;
1562 }
1563
1564 size_t cbActuallyRead = 0;
1565 RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBlock, cbBlock, &cbActuallyRead), VINF_SUCCESS, rcCheck);
1566 if (cbActuallyRead == cbBlock)
1567 {
1568 *poffActual += cbActuallyRead;
1569 return VINF_SUCCESS;
1570 }
1571 RTTestIFailed("RTFileRead at %#RX64 returned just %#x bytes, expected %#x", *poffActual, cbActuallyRead, cbBlock);
1572 *poffActual += cbActuallyRead;
1573 return VERR_READ_ERROR;
1574}
1575
1576
1577void fsPerfIoReadBlockSize(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock)
1578{
1579 RTTestISubF("Sequential read %RU32", cbBlock);
1580
1581 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBlock);
1582 if (pbBuf)
1583 {
1584 memset(pbBuf, 0xf7, cbBlock);
1585 PROFILE_IO_FN("RTFileRead", fsPerfIoReadWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks));
1586 RTMemPageFree(pbBuf, cbBlock);
1587 }
1588 else
1589 RTTestSkipped(g_hTest, "insufficient (virtual) memory available");
1590}
1591
1592
1593DECL_FORCE_INLINE(int) fsPerfIoWriteWorker(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t *pbBlock,
1594 uint64_t *poffActual, uint32_t *pcSeeks)
1595{
1596 /* Do we need to seek back to the start? */
1597 if (*poffActual + cbBlock <= cbFile)
1598 { /* likely */ }
1599 else
1600 {
1601 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
1602 *pcSeeks += 1;
1603 *poffActual = 0;
1604 }
1605
1606 size_t cbActuallyWritten = 0;
1607 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBlock, cbBlock, &cbActuallyWritten), VINF_SUCCESS, rcCheck);
1608 if (cbActuallyWritten == cbBlock)
1609 {
1610 *poffActual += cbActuallyWritten;
1611 return VINF_SUCCESS;
1612 }
1613 RTTestIFailed("RTFileWrite at %#RX64 returned just %#x bytes, expected %#x", *poffActual, cbActuallyWritten, cbBlock);
1614 *poffActual += cbActuallyWritten;
1615 return VERR_WRITE_ERROR;
1616}
1617
1618void fsPerfIoWriteBlockSize(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock)
1619{
1620 RTTestISubF("Sequential write %RU32", cbBlock);
1621
1622 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBlock);
1623 if (pbBuf)
1624 {
1625 memset(pbBuf, 0xf7, cbBlock);
1626 PROFILE_IO_FN("RTFileWrite", fsPerfIoWriteWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks));
1627 RTMemPageFree(pbBuf, cbBlock);
1628 }
1629 else
1630 RTTestSkipped(g_hTest, "insufficient (virtual) memory available");
1631}
1632
1633
1634/**
1635 * This does the read, write and seek tests.
1636 */
1637void fsPerfIo(void)
1638{
1639 RTTestISub("I/O");
1640
1641 /*
1642 * Determin the size of the test file.
1643 */
1644 g_szDir[g_cchDir] = '\0';
1645 RTFOFF cbFree = 0;
1646 RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
1647 uint64_t cbFile = g_cbIoFile;
1648 if (cbFile + _16M < (uint64_t)cbFree)
1649 cbFile = RT_ALIGN_64(cbFile, _64K);
1650 else
1651 {
1652 if (cbFree < _32M)
1653 {
1654 RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 32MB", cbFree);
1655 return;
1656 }
1657 cbFile = cbFree - (cbFree > _128M ? _64M : _16M);
1658 cbFile = RT_ALIGN_64(cbFile, _64K);
1659 RTTestIPrintf(RTTESTLVL_ALWAYS, "Adjusted file size to %'RU64 bytes, due to %'RU64 bytes free.\n", cbFile, cbFree);
1660 }
1661 if (cbFile < _64K)
1662 {
1663 RTTestSkipped(g_hTest, "Specified test file size too small: %'RU64 bytes, requires >= 64KB", cbFile);
1664 return;
1665 }
1666
1667 /*
1668 * Create a cbFile sized test file.
1669 */
1670 RTFILE hFile1;
1671 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file21")),
1672 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
1673 uint8_t *pbFree = NULL;
1674 int rc = fsPerfIoPrepFile(hFile1, cbFile, &pbFree);
1675 RTMemFree(pbFree);
1676 if (RT_SUCCESS(rc))
1677 {
1678 /*
1679 * Do the testing & profiling.
1680 */
1681 if (g_fSeek)
1682 fsPerfIoSeek(hFile1, cbFile);
1683 if (g_fRead)
1684 {
1685 for (unsigned i = 0; i < g_cIoBlocks; i++)
1686 fsPerfIoReadBlockSize(hFile1, cbFile, g_acbIoBlocks[i]);
1687 }
1688 if (g_fWrite)
1689 {
1690 for (unsigned i = 0; i < g_cIoBlocks; i++)
1691 fsPerfIoWriteBlockSize(hFile1, cbFile, g_acbIoBlocks[i]);
1692 }
1693 }
1694
1695 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS);
1696 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
1697 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
1698}
1699
1700
1701static void Usage(PRTSTREAM pStrm)
1702{
1703 char szExec[RTPATH_MAX];
1704 RTStrmPrintf(pStrm, "usage: %s <-d <testdir>> [options]\n",
1705 RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
1706 RTStrmPrintf(pStrm, "\n");
1707 RTStrmPrintf(pStrm, "options: \n");
1708
1709 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
1710 {
1711 char szHelp[80];
1712 const char *pszHelp;
1713 switch (g_aCmdOptions[i].iShort)
1714 {
1715 case 'd': pszHelp = "The directory to use for testing. Default: CWD/fstestdir"; break;
1716 case 'e': pszHelp = "Enables all tests. Default: -e"; break;
1717 case 'z': pszHelp = "Disables all tests. Default: -e"; break;
1718 case 's': pszHelp = "Set benchmark duration in seconds. Default: 10 sec"; break;
1719 case 'm': pszHelp = "Set benchmark duration in milliseconds. Default: 10000 ms"; break;
1720 case 'v': pszHelp = "More verbose execution."; break;
1721 case 'q': pszHelp = "Quiet execution."; break;
1722 case 'h': pszHelp = "Displays this help and exit"; break;
1723 case 'V': pszHelp = "Displays the program revision"; break;
1724 default:
1725 if (g_aCmdOptions[i].iShort >= kCmdOpt_First)
1726 {
1727 if (RTStrStartsWith(g_aCmdOptions[i].pszLong, "--no-"))
1728 RTStrPrintf(szHelp, sizeof(szHelp), "Disables the '%s' test.", g_aCmdOptions[i].pszLong + 5);
1729 else
1730 RTStrPrintf(szHelp, sizeof(szHelp), "Enables the '%s' test.", g_aCmdOptions[i].pszLong + 2);
1731 pszHelp = szHelp;
1732 }
1733 else
1734 pszHelp = "Option undocumented";
1735 break;
1736 }
1737 if ((unsigned)g_aCmdOptions[i].iShort < 127U)
1738 {
1739 char szOpt[64];
1740 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
1741 RTStrmPrintf(pStrm, " %-20s%s\n", szOpt, pszHelp);
1742 }
1743 else
1744 RTStrmPrintf(pStrm, " %-20s%s\n", g_aCmdOptions[i].pszLong, pszHelp);
1745 }
1746}
1747
1748
1749int main(int argc, char *argv[])
1750{
1751 /*
1752 * Init IPRT and globals.
1753 */
1754 int rc = RTTestInitAndCreate("FsPerf", &g_hTest);
1755 if (rc)
1756 return rc;
1757 RTListInit(&g_ManyTreeHead);
1758
1759 /*
1760 * Default values.
1761 */
1762 rc = RTPathGetCurrent(g_szDir, sizeof(g_szDir) / 2);
1763 if (RT_SUCCESS(rc))
1764 rc = RTPathAppend(g_szDir, sizeof(g_szDir) / 2, "fstestdir-");
1765 if (RT_SUCCESS(rc))
1766 {
1767 g_cchDir = strlen(g_szDir);
1768 g_cchDir += RTStrPrintf(&g_szDir[g_cchDir], sizeof(g_szDir) - g_cchDir, "%u" RTPATH_SLASH_STR, RTProcSelf());
1769 }
1770 else
1771 {
1772 RTTestFailed(g_hTest, "RTPathGetCurrent (or RTPathAppend) failed: %Rrc\n", rc);
1773 return RTTestSummaryAndDestroy(g_hTest);
1774 }
1775
1776 RTGETOPTUNION ValueUnion;
1777 RTGETOPTSTATE GetState;
1778 RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
1779 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
1780 {
1781 switch (rc)
1782 {
1783 case 'd':
1784 rc = RTPathAbs(ValueUnion.psz, g_szDir, sizeof(g_szDir) / 2);
1785 if (RT_SUCCESS(rc))
1786 break;
1787 RTTestFailed(g_hTest, "RTPathAbs(%s) failed: %Rrc\n", ValueUnion.psz, rc);
1788 return RTTestSummaryAndDestroy(g_hTest);
1789
1790 case 's':
1791 if (ValueUnion.u32 == 0)
1792 g_nsTestRun = RT_NS_1SEC_64 * 10;
1793 else
1794 g_nsTestRun = ValueUnion.u32 * RT_NS_1SEC_64;
1795 break;
1796
1797 case 'm':
1798 if (ValueUnion.u64 == 0)
1799 g_nsTestRun = RT_NS_1SEC_64 * 10;
1800 else
1801 g_nsTestRun = ValueUnion.u64 * RT_NS_1MS;
1802 break;
1803
1804 case 'e':
1805 g_fManyFiles = true;
1806 g_fOpen = true;
1807 g_fFStat = true;
1808 g_fFChMod = true;
1809 g_fFUtimes = true;
1810 g_fStat = true;
1811 g_fChMod = true;
1812 g_fUtimes = true;
1813 g_fRename = true;
1814 g_fDirEnum = true;
1815 g_fMkRmDir = true;
1816 g_fStatVfs = true;
1817 g_fRm = true;
1818 g_fChSize = true;
1819 g_fRead = true;
1820 g_fWrite = true;
1821 g_fSeek = true;
1822 break;
1823
1824 case 'z':
1825 g_fManyFiles = false;
1826 g_fOpen = false;
1827 g_fFStat = false;
1828 g_fFChMod = false;
1829 g_fFUtimes = false;
1830 g_fStat = false;
1831 g_fChMod = false;
1832 g_fUtimes = false;
1833 g_fRename = false;
1834 g_fDirEnum = false;
1835 g_fMkRmDir = false;
1836 g_fStatVfs = false;
1837 g_fRm = false;
1838 g_fChSize = false;
1839 g_fRead = false;
1840 g_fWrite = false;
1841 g_fSeek = false;
1842 break;
1843
1844#define CASE_OPT(a_Stem) \
1845 case RT_CONCAT(kCmdOpt_,a_Stem): RT_CONCAT(g_f,a_Stem) = true; break; \
1846 case RT_CONCAT(kCmdOpt_No,a_Stem): RT_CONCAT(g_f,a_Stem) = false; break
1847 CASE_OPT(ManyFiles);
1848 CASE_OPT(Open);
1849 CASE_OPT(FStat);
1850 CASE_OPT(FChMod);
1851 CASE_OPT(FUtimes);
1852 CASE_OPT(Stat);
1853 CASE_OPT(ChMod);
1854 CASE_OPT(Utimes);
1855 CASE_OPT(Rename);
1856 CASE_OPT(DirEnum);
1857 CASE_OPT(MkRmDir);
1858 CASE_OPT(StatVfs);
1859 CASE_OPT(Rm);
1860 CASE_OPT(ChSize);
1861 CASE_OPT(Seek);
1862 CASE_OPT(Read);
1863 CASE_OPT(Write);
1864#undef CASE_OPT
1865
1866 case 'q':
1867 g_uVerbosity = 0;
1868 break;
1869
1870 case 'v':
1871 g_uVerbosity++;
1872 break;
1873
1874 case 'h':
1875 Usage(g_pStdOut);
1876 return RTEXITCODE_SUCCESS;
1877
1878 case 'V':
1879 {
1880 char szRev[] = "$Revision: 76897 $";
1881 szRev[RT_ELEMENTS(szRev) - 2] = '\0';
1882 RTPrintf(RTStrStrip(strchr(szRev, ':') + 1));
1883 return RTEXITCODE_SUCCESS;
1884 }
1885
1886 default:
1887 return RTGetOptPrintError(rc, &ValueUnion);
1888 }
1889 }
1890
1891 /*
1892 * Create the test directory with an 'empty' subdirectory under it,
1893 * execute the tests, and remove directory when done.
1894 */
1895 RTTestBanner(g_hTest);
1896 if (!RTPathExists(g_szDir))
1897 {
1898 /* The base dir: */
1899 rc = RTDirCreate(g_szDir, 0755,
1900 RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
1901 if (RT_SUCCESS(rc))
1902 {
1903 RTTestIPrintf(RTTESTLVL_ALWAYS, "Test dir: %s\n", g_szDir);
1904 rc = fsPrepTestArea();
1905 if (RT_SUCCESS(rc))
1906 {
1907 /* Profile RTTimeNanoTS(). */
1908 fsPerfNanoTS();
1909
1910 /* Do tests: */
1911 if (g_fManyFiles)
1912 fsPerfManyFiles();
1913 if (g_fOpen)
1914 fsPerfOpen();
1915 if (g_fFStat)
1916 fsPerfFStat();
1917 if (g_fFChMod)
1918 fsPerfFChMod();
1919 if (g_fFUtimes)
1920 fsPerfFUtimes();
1921 if (g_fStat)
1922 fsPerfStat();
1923 if (g_fChMod)
1924 fsPerfChmod();
1925 if (g_fUtimes)
1926 fsPerfUtimes();
1927 if (g_fRename)
1928 fsPerfRename();
1929 if (g_fDirEnum)
1930 vsPerfDirEnum();
1931 if (g_fMkRmDir)
1932 fsPerfMkRmDir();
1933 if (g_fStatVfs)
1934 fsPerfStatVfs();
1935 if (g_fRm || g_fManyFiles)
1936 fsPerfRm(); /* deletes manyfiles and manytree */
1937 if (g_fChSize)
1938 fsPerfChSize();
1939 if (g_fRead || g_fWrite || g_fSeek)
1940 fsPerfIo();
1941 }
1942
1943 /* Cleanup: */
1944 g_szDir[g_cchDir] = '\0';
1945 rc = RTDirRemoveRecursive(g_szDir, RTDIRRMREC_F_CONTENT_AND_DIR);
1946 if (RT_FAILURE(rc))
1947 RTTestFailed(g_hTest, "RTDirRemoveRecursive(%s,) -> %Rrc\n", g_szDir, rc);
1948 }
1949 else
1950 RTTestFailed(g_hTest, "RTDirCreate(%s) -> %Rrc\n", g_szDir, rc);
1951 }
1952 else
1953 RTTestFailed(g_hTest, "Test directory already exists: %s\n", g_szDir);
1954
1955 return RTTestSummaryAndDestroy(g_hTest);
1956}
Note: See TracBrowser for help on using the repository browser.

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