VirtualBox

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

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

FsPerf: Enabled mmap tests on darwin. bugref:9172

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

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