VirtualBox

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

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