VirtualBox

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

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

FsPerf,iprt/nt/nt.h: Buffer size testcase for NtQueryVolumeInformationFile. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 280.7 KB
Line 
1/* $Id: FsPerf.cpp 78535 2019-05-15 23:48:14Z 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#ifdef RT_OS_OS2
32# define INCL_BASE
33# include <os2.h>
34# undef RT_MAX
35#endif
36#include <iprt/alloca.h>
37#include <iprt/asm.h>
38#include <iprt/assert.h>
39#include <iprt/err.h>
40#include <iprt/dir.h>
41#include <iprt/file.h>
42#include <iprt/getopt.h>
43#include <iprt/initterm.h>
44#include <iprt/list.h>
45#include <iprt/mem.h>
46#include <iprt/message.h>
47#include <iprt/param.h>
48#include <iprt/path.h>
49#ifdef RT_OS_LINUX
50# include <iprt/pipe.h>
51#endif
52#include <iprt/process.h>
53#include <iprt/rand.h>
54#include <iprt/string.h>
55#include <iprt/stream.h>
56#include <iprt/system.h>
57#include <iprt/tcp.h>
58#include <iprt/test.h>
59#include <iprt/time.h>
60#include <iprt/thread.h>
61#include <iprt/zero.h>
62
63#ifdef RT_OS_WINDOWS
64# include <iprt/nt/nt-and-windows.h>
65#else
66# include <errno.h>
67# include <unistd.h>
68# include <limits.h>
69# include <sys/types.h>
70# include <sys/fcntl.h>
71# ifndef RT_OS_OS2
72# include <sys/mman.h>
73# include <sys/uio.h>
74# endif
75# include <sys/socket.h>
76# include <signal.h>
77# ifdef RT_OS_LINUX
78# include <sys/sendfile.h>
79# include <sys/syscall.h>
80# endif
81# ifdef RT_OS_DARWIN
82# include <sys/uio.h>
83# endif
84#endif
85
86
87/*********************************************************************************************************************************
88* Defined Constants And Macros *
89*********************************************************************************************************************************/
90/** Used for cutting the the -d parameter value short and avoid a number of buffer overflow checks. */
91#define FSPERF_MAX_NEEDED_PATH 224
92/** The max path used by this code.
93 * It greatly exceeds the RTPATH_MAX so we can push the limits on windows. */
94#define FSPERF_MAX_PATH (_32K)
95
96/** EOF marker character used by the master/slave comms. */
97#define FSPERF_EOF 0x1a
98/** EOF marker character used by the master/slave comms, string version. */
99#define FSPERF_EOF_STR "\x1a"
100
101/** @def FSPERF_TEST_SENDFILE
102 * Whether to enable the sendfile() tests. */
103#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN)
104# define FSPERF_TEST_SENDFILE
105#endif
106
107/**
108 * Macro for profiling @a a_fnCall (typically forced inline) for about @a a_cNsTarget ns.
109 *
110 * Always does an even number of iterations.
111 */
112#define PROFILE_FN(a_fnCall, a_cNsTarget, a_szDesc) \
113 do { \
114 /* Estimate how many iterations we need to fill up the given timeslot: */ \
115 fsPerfYield(); \
116 uint64_t nsStart = RTTimeNanoTS(); \
117 uint64_t nsPrf; \
118 do \
119 nsPrf = RTTimeNanoTS(); \
120 while (nsPrf == nsStart); \
121 nsStart = nsPrf; \
122 \
123 uint64_t iIteration = 0; \
124 do \
125 { \
126 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
127 iIteration++; \
128 nsPrf = RTTimeNanoTS() - nsStart; \
129 } while (nsPrf < RT_NS_10MS || (iIteration & 1)); \
130 nsPrf /= iIteration; \
131 if (nsPrf > g_nsPerNanoTSCall + 32) \
132 nsPrf -= g_nsPerNanoTSCall; \
133 \
134 uint64_t cIterations = (a_cNsTarget) / nsPrf; \
135 if (cIterations <= 1) \
136 cIterations = 2; \
137 else if (cIterations & 1) \
138 cIterations++; \
139 \
140 /* Do the actual profiling: */ \
141 fsPerfYield(); \
142 iIteration = 0; \
143 nsStart = RTTimeNanoTS(); \
144 for (; iIteration < cIterations; iIteration++) \
145 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
146 nsPrf = RTTimeNanoTS() - nsStart; \
147 RTTestIValue(a_szDesc, nsPrf / cIterations, RTTESTUNIT_NS_PER_OCCURRENCE); \
148 if (g_fShowDuration) \
149 RTTestIValueF(nsPrf, RTTESTUNIT_NS, "%s duration", a_szDesc); \
150 if (g_fShowIterations) \
151 RTTestIValueF(iIteration, RTTESTUNIT_OCCURRENCES, "%s iterations", a_szDesc); \
152 } while (0)
153
154
155/**
156 * Macro for profiling an operation on each file in the manytree directory tree.
157 *
158 * Always does an even number of tree iterations.
159 */
160#define PROFILE_MANYTREE_FN(a_szPath, a_fnCall, a_cEstimationIterations, a_cNsTarget, a_szDesc) \
161 do { \
162 if (!g_fManyFiles) \
163 break; \
164 \
165 /* Estimate how many iterations we need to fill up the given timeslot: */ \
166 fsPerfYield(); \
167 uint64_t nsStart = RTTimeNanoTS(); \
168 uint64_t ns; \
169 do \
170 ns = RTTimeNanoTS(); \
171 while (ns == nsStart); \
172 nsStart = ns; \
173 \
174 PFSPERFNAMEENTRY pCur; \
175 uint64_t iIteration = 0; \
176 do \
177 { \
178 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
179 { \
180 memcpy(a_szPath, pCur->szName, pCur->cchName); \
181 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
182 { \
183 RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
184 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
185 } \
186 } \
187 iIteration++; \
188 ns = RTTimeNanoTS() - nsStart; \
189 } while (ns < RT_NS_10MS || (iIteration & 1)); \
190 ns /= iIteration; \
191 if (ns > g_nsPerNanoTSCall + 32) \
192 ns -= g_nsPerNanoTSCall; \
193 \
194 uint32_t cIterations = (a_cNsTarget) / ns; \
195 if (cIterations <= 1) \
196 cIterations = 2; \
197 else if (cIterations & 1) \
198 cIterations++; \
199 \
200 /* Do the actual profiling: */ \
201 fsPerfYield(); \
202 uint32_t cCalls = 0; \
203 nsStart = RTTimeNanoTS(); \
204 for (iIteration = 0; iIteration < cIterations; iIteration++) \
205 { \
206 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
207 { \
208 memcpy(a_szPath, pCur->szName, pCur->cchName); \
209 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
210 { \
211 RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
212 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
213 cCalls++; \
214 } \
215 } \
216 } \
217 ns = RTTimeNanoTS() - nsStart; \
218 RTTestIValueF(ns / cCalls, RTTESTUNIT_NS_PER_OCCURRENCE, a_szDesc); \
219 if (g_fShowDuration) \
220 RTTestIValueF(ns, RTTESTUNIT_NS, "%s duration", a_szDesc); \
221 if (g_fShowIterations) \
222 RTTestIValueF(iIteration, RTTESTUNIT_OCCURRENCES, "%s iterations", a_szDesc); \
223 } while (0)
224
225
226/**
227 * Execute a_fnCall for each file in the manytree.
228 */
229#define DO_MANYTREE_FN(a_szPath, a_fnCall) \
230 do { \
231 PFSPERFNAMEENTRY pCur; \
232 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry) \
233 { \
234 memcpy(a_szPath, pCur->szName, pCur->cchName); \
235 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++) \
236 { \
237 RTStrFormatU32(&a_szPath[pCur->cchName], sizeof(a_szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD); \
238 a_fnCall; \
239 } \
240 } \
241 } while (0)
242
243
244/** @def FSPERF_VERR_PATH_NOT_FOUND
245 * Hides the fact that we only get VERR_PATH_NOT_FOUND on non-unix systems. */
246#if defined(RT_OS_WINDOWS) //|| defined(RT_OS_OS2) - using posix APIs IIRC, so lost in translation.
247# define FSPERF_VERR_PATH_NOT_FOUND VERR_PATH_NOT_FOUND
248#else
249# define FSPERF_VERR_PATH_NOT_FOUND VERR_FILE_NOT_FOUND
250#endif
251
252
253/*********************************************************************************************************************************
254* Structures and Typedefs *
255*********************************************************************************************************************************/
256typedef struct FSPERFNAMEENTRY
257{
258 RTLISTNODE Entry;
259 uint16_t cchName;
260 char szName[RT_FLEXIBLE_ARRAY];
261} FSPERFNAMEENTRY;
262typedef FSPERFNAMEENTRY *PFSPERFNAMEENTRY;
263
264
265enum
266{
267 kCmdOpt_First = 128,
268
269 kCmdOpt_ManyFiles = kCmdOpt_First,
270 kCmdOpt_NoManyFiles,
271 kCmdOpt_Open,
272 kCmdOpt_NoOpen,
273 kCmdOpt_FStat,
274 kCmdOpt_NoFStat,
275#ifdef RT_OS_WINDOWS
276 kCmdOpt_NtQueryInfoFile,
277 kCmdOpt_NoNtQueryInfoFile,
278 kCmdOpt_NtQueryVolInfoFile,
279 kCmdOpt_NoNtQueryVolInfoFile,
280#endif
281 kCmdOpt_FChMod,
282 kCmdOpt_NoFChMod,
283 kCmdOpt_FUtimes,
284 kCmdOpt_NoFUtimes,
285 kCmdOpt_Stat,
286 kCmdOpt_NoStat,
287 kCmdOpt_ChMod,
288 kCmdOpt_NoChMod,
289 kCmdOpt_Utimes,
290 kCmdOpt_NoUtimes,
291 kCmdOpt_Rename,
292 kCmdOpt_NoRename,
293 kCmdOpt_DirOpen,
294 kCmdOpt_NoDirOpen,
295 kCmdOpt_DirEnum,
296 kCmdOpt_NoDirEnum,
297 kCmdOpt_MkRmDir,
298 kCmdOpt_NoMkRmDir,
299 kCmdOpt_StatVfs,
300 kCmdOpt_NoStatVfs,
301 kCmdOpt_Rm,
302 kCmdOpt_NoRm,
303 kCmdOpt_ChSize,
304 kCmdOpt_NoChSize,
305 kCmdOpt_ReadPerf,
306 kCmdOpt_NoReadPerf,
307 kCmdOpt_ReadTests,
308 kCmdOpt_NoReadTests,
309#ifdef FSPERF_TEST_SENDFILE
310 kCmdOpt_SendFile,
311 kCmdOpt_NoSendFile,
312#endif
313#ifdef RT_OS_LINUX
314 kCmdOpt_Splice,
315 kCmdOpt_NoSplice,
316#endif
317 kCmdOpt_WritePerf,
318 kCmdOpt_NoWritePerf,
319 kCmdOpt_WriteTests,
320 kCmdOpt_NoWriteTests,
321 kCmdOpt_Seek,
322 kCmdOpt_NoSeek,
323 kCmdOpt_FSync,
324 kCmdOpt_NoFSync,
325 kCmdOpt_MMap,
326 kCmdOpt_NoMMap,
327 kCmdOpt_IgnoreNoCache,
328 kCmdOpt_NoIgnoreNoCache,
329 kCmdOpt_IoFileSize,
330 kCmdOpt_SetBlockSize,
331 kCmdOpt_AddBlockSize,
332 kCmdOpt_Copy,
333 kCmdOpt_NoCopy,
334 kCmdOpt_Remote,
335 kCmdOpt_NoRemote,
336
337 kCmdOpt_ShowDuration,
338 kCmdOpt_NoShowDuration,
339 kCmdOpt_ShowIterations,
340 kCmdOpt_NoShowIterations,
341
342 kCmdOpt_ManyTreeFilesPerDir,
343 kCmdOpt_ManyTreeSubdirsPerDir,
344 kCmdOpt_ManyTreeDepth,
345
346 kCmdOpt_End
347};
348
349
350/*********************************************************************************************************************************
351* Global Variables *
352*********************************************************************************************************************************/
353/** Command line parameters */
354static const RTGETOPTDEF g_aCmdOptions[] =
355{
356 { "--dir", 'd', RTGETOPT_REQ_STRING },
357 { "--relative-dir", 'r', RTGETOPT_REQ_NOTHING },
358 { "--comms-dir", 'c', RTGETOPT_REQ_STRING },
359 { "--comms-slave", 'C', RTGETOPT_REQ_NOTHING },
360 { "--seconds", 's', RTGETOPT_REQ_UINT32 },
361 { "--milliseconds", 'm', RTGETOPT_REQ_UINT64 },
362
363 { "--enable-all", 'e', RTGETOPT_REQ_NOTHING },
364 { "--disable-all", 'z', RTGETOPT_REQ_NOTHING },
365
366 { "--many-files", kCmdOpt_ManyFiles, RTGETOPT_REQ_UINT32 },
367 { "--no-many-files", kCmdOpt_NoManyFiles, RTGETOPT_REQ_NOTHING },
368 { "--files-per-dir", kCmdOpt_ManyTreeFilesPerDir, RTGETOPT_REQ_UINT32 },
369 { "--subdirs-per-dir", kCmdOpt_ManyTreeSubdirsPerDir, RTGETOPT_REQ_UINT32 },
370 { "--tree-depth", kCmdOpt_ManyTreeDepth, RTGETOPT_REQ_UINT32 },
371
372 { "--open", kCmdOpt_Open, RTGETOPT_REQ_NOTHING },
373 { "--no-open", kCmdOpt_NoOpen, RTGETOPT_REQ_NOTHING },
374 { "--fstat", kCmdOpt_FStat, RTGETOPT_REQ_NOTHING },
375 { "--no-fstat", kCmdOpt_NoFStat, RTGETOPT_REQ_NOTHING },
376#ifdef RT_OS_WINDOWS
377 { "--nt-query-info-file", kCmdOpt_NtQueryInfoFile, RTGETOPT_REQ_NOTHING },
378 { "--no-nt-query-info-file", kCmdOpt_NoNtQueryInfoFile, RTGETOPT_REQ_NOTHING },
379 { "--nt-query-vol-info-file", kCmdOpt_NtQueryVolInfoFile, RTGETOPT_REQ_NOTHING },
380 { "--no-nt-query-vol-info-file",kCmdOpt_NoNtQueryVolInfoFile, RTGETOPT_REQ_NOTHING },
381#endif
382 { "--fchmod", kCmdOpt_FChMod, RTGETOPT_REQ_NOTHING },
383 { "--no-fchmod", kCmdOpt_NoFChMod, RTGETOPT_REQ_NOTHING },
384 { "--futimes", kCmdOpt_FUtimes, RTGETOPT_REQ_NOTHING },
385 { "--no-futimes", kCmdOpt_NoFUtimes, RTGETOPT_REQ_NOTHING },
386 { "--stat", kCmdOpt_Stat, RTGETOPT_REQ_NOTHING },
387 { "--no-stat", kCmdOpt_NoStat, RTGETOPT_REQ_NOTHING },
388 { "--chmod", kCmdOpt_ChMod, RTGETOPT_REQ_NOTHING },
389 { "--no-chmod", kCmdOpt_NoChMod, RTGETOPT_REQ_NOTHING },
390 { "--utimes", kCmdOpt_Utimes, RTGETOPT_REQ_NOTHING },
391 { "--no-utimes", kCmdOpt_NoUtimes, RTGETOPT_REQ_NOTHING },
392 { "--rename", kCmdOpt_Rename, RTGETOPT_REQ_NOTHING },
393 { "--no-rename", kCmdOpt_NoRename, RTGETOPT_REQ_NOTHING },
394 { "--dir-open", kCmdOpt_DirOpen, RTGETOPT_REQ_NOTHING },
395 { "--no-dir-open", kCmdOpt_NoDirOpen, RTGETOPT_REQ_NOTHING },
396 { "--dir-enum", kCmdOpt_DirEnum, RTGETOPT_REQ_NOTHING },
397 { "--no-dir-enum", kCmdOpt_NoDirEnum, RTGETOPT_REQ_NOTHING },
398 { "--mk-rm-dir", kCmdOpt_MkRmDir, RTGETOPT_REQ_NOTHING },
399 { "--no-mk-rm-dir", kCmdOpt_NoMkRmDir, RTGETOPT_REQ_NOTHING },
400 { "--stat-vfs", kCmdOpt_StatVfs, RTGETOPT_REQ_NOTHING },
401 { "--no-stat-vfs", kCmdOpt_NoStatVfs, RTGETOPT_REQ_NOTHING },
402 { "--rm", kCmdOpt_Rm, RTGETOPT_REQ_NOTHING },
403 { "--no-rm", kCmdOpt_NoRm, RTGETOPT_REQ_NOTHING },
404 { "--chsize", kCmdOpt_ChSize, RTGETOPT_REQ_NOTHING },
405 { "--no-chsize", kCmdOpt_NoChSize, RTGETOPT_REQ_NOTHING },
406 { "--read-tests", kCmdOpt_ReadTests, RTGETOPT_REQ_NOTHING },
407 { "--no-read-tests", kCmdOpt_NoReadTests, RTGETOPT_REQ_NOTHING },
408 { "--read-perf", kCmdOpt_ReadPerf, RTGETOPT_REQ_NOTHING },
409 { "--no-read-perf", kCmdOpt_NoReadPerf, RTGETOPT_REQ_NOTHING },
410#ifdef FSPERF_TEST_SENDFILE
411 { "--sendfile", kCmdOpt_SendFile, RTGETOPT_REQ_NOTHING },
412 { "--no-sendfile", kCmdOpt_NoSendFile, RTGETOPT_REQ_NOTHING },
413#endif
414#ifdef RT_OS_LINUX
415 { "--splice", kCmdOpt_Splice, RTGETOPT_REQ_NOTHING },
416 { "--no-splice", kCmdOpt_NoSplice, RTGETOPT_REQ_NOTHING },
417#endif
418 { "--write-tests", kCmdOpt_WriteTests, RTGETOPT_REQ_NOTHING },
419 { "--no-write-tests", kCmdOpt_NoWriteTests, RTGETOPT_REQ_NOTHING },
420 { "--write-perf", kCmdOpt_WritePerf, RTGETOPT_REQ_NOTHING },
421 { "--no-write-perf", kCmdOpt_NoWritePerf, RTGETOPT_REQ_NOTHING },
422 { "--seek", kCmdOpt_Seek, RTGETOPT_REQ_NOTHING },
423 { "--no-seek", kCmdOpt_NoSeek, RTGETOPT_REQ_NOTHING },
424 { "--fsync", kCmdOpt_FSync, RTGETOPT_REQ_NOTHING },
425 { "--no-fsync", kCmdOpt_NoFSync, RTGETOPT_REQ_NOTHING },
426 { "--mmap", kCmdOpt_MMap, RTGETOPT_REQ_NOTHING },
427 { "--no-mmap", kCmdOpt_NoMMap, RTGETOPT_REQ_NOTHING },
428 { "--ignore-no-cache", kCmdOpt_IgnoreNoCache, RTGETOPT_REQ_NOTHING },
429 { "--no-ignore-no-cache", kCmdOpt_NoIgnoreNoCache, RTGETOPT_REQ_NOTHING },
430 { "--io-file-size", kCmdOpt_IoFileSize, RTGETOPT_REQ_UINT64 },
431 { "--set-block-size", kCmdOpt_SetBlockSize, RTGETOPT_REQ_UINT32 },
432 { "--add-block-size", kCmdOpt_AddBlockSize, RTGETOPT_REQ_UINT32 },
433 { "--copy", kCmdOpt_Copy, RTGETOPT_REQ_NOTHING },
434 { "--no-copy", kCmdOpt_NoCopy, RTGETOPT_REQ_NOTHING },
435 { "--remote", kCmdOpt_Remote, RTGETOPT_REQ_NOTHING },
436 { "--no-remote", kCmdOpt_NoRemote, RTGETOPT_REQ_NOTHING },
437
438 { "--show-duration", kCmdOpt_ShowDuration, RTGETOPT_REQ_NOTHING },
439 { "--no-show-duration", kCmdOpt_NoShowDuration, RTGETOPT_REQ_NOTHING },
440 { "--show-iterations", kCmdOpt_ShowIterations, RTGETOPT_REQ_NOTHING },
441 { "--no-show-iterations", kCmdOpt_NoShowIterations, RTGETOPT_REQ_NOTHING },
442
443 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
444 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
445 { "--version", 'V', RTGETOPT_REQ_NOTHING },
446 { "--help", 'h', RTGETOPT_REQ_NOTHING } /* for Usage() */
447};
448
449/** The test handle. */
450static RTTEST g_hTest;
451/** The number of nanoseconds a RTTimeNanoTS call takes.
452 * This is used for adjusting loop count estimates. */
453static uint64_t g_nsPerNanoTSCall = 1;
454/** Whether or not to display the duration of each profile run.
455 * This is chiefly for verify the estimate phase. */
456static bool g_fShowDuration = false;
457/** Whether or not to display the iteration count for each profile run.
458 * This is chiefly for verify the estimate phase. */
459static bool g_fShowIterations = false;
460/** Verbosity level. */
461static uint32_t g_uVerbosity = 0;
462
463/** @name Selected subtest
464 * @{ */
465static bool g_fManyFiles = true;
466static bool g_fOpen = true;
467static bool g_fFStat = true;
468#ifdef RT_OS_WINDOWS
469static bool g_fNtQueryInfoFile = true;
470static bool g_fNtQueryVolInfoFile = true;
471#endif
472static bool g_fFChMod = true;
473static bool g_fFUtimes = true;
474static bool g_fStat = true;
475static bool g_fChMod = true;
476static bool g_fUtimes = true;
477static bool g_fRename = true;
478static bool g_fDirOpen = true;
479static bool g_fDirEnum = true;
480static bool g_fMkRmDir = true;
481static bool g_fStatVfs = true;
482static bool g_fRm = true;
483static bool g_fChSize = true;
484static bool g_fReadTests = true;
485static bool g_fReadPerf = true;
486#ifdef FSPERF_TEST_SENDFILE
487static bool g_fSendFile = true;
488#endif
489#ifdef RT_OS_LINUX
490static bool g_fSplice = true;
491#endif
492static bool g_fWriteTests = true;
493static bool g_fWritePerf = true;
494static bool g_fSeek = true;
495static bool g_fFSync = true;
496static bool g_fMMap = true;
497static bool g_fCopy = true;
498static bool g_fRemote = true;
499/** @} */
500
501/** The length of each test run. */
502static uint64_t g_nsTestRun = RT_NS_1SEC_64 * 10;
503
504/** For the 'manyfiles' subdir. */
505static uint32_t g_cManyFiles = 10000;
506
507/** Number of files in the 'manytree' directory tree. */
508static uint32_t g_cManyTreeFiles = 640 + 16*640 /*10880*/;
509/** Number of files per directory in the 'manytree' construct. */
510static uint32_t g_cManyTreeFilesPerDir = 640;
511/** Number of subdirs per directory in the 'manytree' construct. */
512static uint32_t g_cManyTreeSubdirsPerDir = 16;
513/** The depth of the 'manytree' directory tree. */
514static uint32_t g_cManyTreeDepth = 1;
515/** List of directories in the many tree, creation order. */
516static RTLISTANCHOR g_ManyTreeHead;
517
518/** Number of configured I/O block sizes. */
519static uint32_t g_cIoBlocks = 8;
520/** Configured I/O block sizes. */
521static uint32_t g_acbIoBlocks[16] = { 1, 512, 4096, 16384, 65536, _1M, _32M, _128M };
522/** The desired size of the test file we use for I/O. */
523static uint64_t g_cbIoFile = _512M;
524/** Whether to be less strict with non-cache file handle. */
525static bool g_fIgnoreNoCache = false;
526
527/** Set if g_szDir and friends are path relative to CWD rather than absolute. */
528static bool g_fRelativeDir = false;
529/** The length of g_szDir. */
530static size_t g_cchDir;
531/** The length of g_szEmptyDir. */
532static size_t g_cchEmptyDir;
533/** The length of g_szDeepDir. */
534static size_t g_cchDeepDir;
535
536/** The length of g_szCommsDir. */
537static size_t g_cchCommsDir;
538/** The length of g_szCommsSubDir. */
539static size_t g_cchCommsSubDir;
540
541/** The test directory (absolute). This will always have a trailing slash. */
542static char g_szDir[FSPERF_MAX_PATH];
543/** The test directory (absolute), 2nd copy for use with InDir2(). */
544static char g_szDir2[FSPERF_MAX_PATH];
545/** The empty test directory (absolute). This will always have a trailing slash. */
546static char g_szEmptyDir[FSPERF_MAX_PATH];
547/** The deep test directory (absolute). This will always have a trailing slash. */
548static char g_szDeepDir[FSPERF_MAX_PATH + _1K];
549
550/** The communcations directory. This will always have a trailing slash. */
551static char g_szCommsDir[FSPERF_MAX_PATH];
552/** The communcations subdirectory used for the actual communication. This will
553 * always have a trailing slash. */
554static char g_szCommsSubDir[FSPERF_MAX_PATH];
555
556/**
557 * Yield the CPU and stuff before starting a test run.
558 */
559DECLINLINE(void) fsPerfYield(void)
560{
561 RTThreadYield();
562 RTThreadYield();
563}
564
565
566/**
567 * Profiles the RTTimeNanoTS call, setting g_nsPerNanoTSCall.
568 */
569static void fsPerfNanoTS(void)
570{
571 fsPerfYield();
572
573 /* Make sure we start off on a changing timestamp on platforms will low time resoultion. */
574 uint64_t nsStart = RTTimeNanoTS();
575 uint64_t ns;
576 do
577 ns = RTTimeNanoTS();
578 while (ns == nsStart);
579 nsStart = ns;
580
581 /* Call it for 10 ms. */
582 uint32_t i = 0;
583 do
584 {
585 i++;
586 ns = RTTimeNanoTS();
587 }
588 while (ns - nsStart < RT_NS_10MS);
589
590 g_nsPerNanoTSCall = (ns - nsStart) / i;
591}
592
593
594/**
595 * Construct a path relative to the base test directory.
596 *
597 * @returns g_szDir.
598 * @param pszAppend What to append.
599 * @param cchAppend How much to append.
600 */
601DECLINLINE(char *) InDir(const char *pszAppend, size_t cchAppend)
602{
603 Assert(g_szDir[g_cchDir - 1] == RTPATH_SLASH);
604 memcpy(&g_szDir[g_cchDir], pszAppend, cchAppend);
605 g_szDir[g_cchDir + cchAppend] = '\0';
606 return &g_szDir[0];
607}
608
609
610/**
611 * Construct a path relative to the base test directory, 2nd copy.
612 *
613 * @returns g_szDir2.
614 * @param pszAppend What to append.
615 * @param cchAppend How much to append.
616 */
617DECLINLINE(char *) InDir2(const char *pszAppend, size_t cchAppend)
618{
619 Assert(g_szDir[g_cchDir - 1] == RTPATH_SLASH);
620 memcpy(g_szDir2, g_szDir, g_cchDir);
621 memcpy(&g_szDir2[g_cchDir], pszAppend, cchAppend);
622 g_szDir2[g_cchDir + cchAppend] = '\0';
623 return &g_szDir2[0];
624}
625
626
627/**
628 * Construct a path relative to the empty directory.
629 *
630 * @returns g_szEmptyDir.
631 * @param pszAppend What to append.
632 * @param cchAppend How much to append.
633 */
634DECLINLINE(char *) InEmptyDir(const char *pszAppend, size_t cchAppend)
635{
636 Assert(g_szEmptyDir[g_cchEmptyDir - 1] == RTPATH_SLASH);
637 memcpy(&g_szEmptyDir[g_cchEmptyDir], pszAppend, cchAppend);
638 g_szEmptyDir[g_cchEmptyDir + cchAppend] = '\0';
639 return &g_szEmptyDir[0];
640}
641
642
643/**
644 * Construct a path relative to the deep test directory.
645 *
646 * @returns g_szDeepDir.
647 * @param pszAppend What to append.
648 * @param cchAppend How much to append.
649 */
650DECLINLINE(char *) InDeepDir(const char *pszAppend, size_t cchAppend)
651{
652 Assert(g_szDeepDir[g_cchDeepDir - 1] == RTPATH_SLASH);
653 memcpy(&g_szDeepDir[g_cchDeepDir], pszAppend, cchAppend);
654 g_szDeepDir[g_cchDeepDir + cchAppend] = '\0';
655 return &g_szDeepDir[0];
656}
657
658
659
660/*********************************************************************************************************************************
661* Slave FsPerf Instance Interaction. *
662*********************************************************************************************************************************/
663
664/**
665 * Construct a path relative to the comms directory.
666 *
667 * @returns g_szCommsDir.
668 * @param pszAppend What to append.
669 * @param cchAppend How much to append.
670 */
671DECLINLINE(char *) InCommsDir(const char *pszAppend, size_t cchAppend)
672{
673 Assert(g_szCommsDir[g_cchCommsDir - 1] == RTPATH_SLASH);
674 memcpy(&g_szCommsDir[g_cchCommsDir], pszAppend, cchAppend);
675 g_szCommsDir[g_cchCommsDir + cchAppend] = '\0';
676 return &g_szCommsDir[0];
677}
678
679
680/**
681 * Construct a path relative to the comms sub-directory.
682 *
683 * @returns g_szCommsSubDir.
684 * @param pszAppend What to append.
685 * @param cchAppend How much to append.
686 */
687DECLINLINE(char *) InCommsSubDir(const char *pszAppend, size_t cchAppend)
688{
689 Assert(g_szCommsSubDir[g_cchCommsSubDir - 1] == RTPATH_SLASH);
690 memcpy(&g_szCommsSubDir[g_cchCommsSubDir], pszAppend, cchAppend);
691 g_szCommsSubDir[g_cchCommsSubDir + cchAppend] = '\0';
692 return &g_szCommsSubDir[0];
693}
694
695
696/**
697 * Creates a file under g_szCommsDir with the given content.
698 *
699 * Will modify g_szCommsDir to contain the given filename.
700 *
701 * @returns IPRT status code (fully bitched).
702 * @param pszFilename The filename.
703 * @param cchFilename The length of the filename.
704 * @param pszContent The file content.
705 * @param cchContent The length of the file content.
706 */
707static int FsPerfCommsWriteFile(const char *pszFilename, size_t cchFilename, const char *pszContent, size_t cchContent)
708{
709 RTFILE hFile;
710 int rc = RTFileOpen(&hFile, InCommsDir(pszFilename, cchFilename),
711 RTFILE_O_WRITE | RTFILE_O_DENY_NONE | RTFILE_O_CREATE_REPLACE);
712 if (RT_SUCCESS(rc))
713 {
714 rc = RTFileWrite(hFile, pszContent, cchContent, NULL);
715 if (RT_FAILURE(rc))
716 RTMsgError("Error writing %#zx bytes to '%s': %Rrc", cchContent, g_szCommsDir, rc);
717
718 int rc2 = RTFileClose(hFile);
719 if (RT_FAILURE(rc2))
720 {
721 RTMsgError("Error closing to '%s': %Rrc", g_szCommsDir, rc);
722 rc = rc2;
723 }
724 if (RT_SUCCESS(rc) && g_uVerbosity >= 3)
725 RTMsgInfo("comms: wrote '%s'\n", g_szCommsDir);
726 if (RT_FAILURE(rc))
727 RTFileDelete(g_szCommsDir);
728 }
729 else
730 RTMsgError("Failed to create '%s': %Rrc", g_szCommsDir, rc);
731 return rc;
732}
733
734
735/**
736 * Creates a file under g_szCommsDir with the given content, then renames it
737 * into g_szCommsSubDir.
738 *
739 * Will modify g_szCommsSubDir to contain the final filename and g_szCommsDir to
740 * hold the temporary one.
741 *
742 * @returns IPRT status code (fully bitched).
743 * @param pszFilename The filename.
744 * @param cchFilename The length of the filename.
745 * @param pszContent The file content.
746 * @param cchContent The length of the file content.
747 */
748static int FsPerfCommsWriteFileAndRename(const char *pszFilename, size_t cchFilename, const char *pszContent, size_t cchContent)
749{
750 int rc = FsPerfCommsWriteFile(pszFilename, cchFilename, pszContent, cchContent);
751 if (RT_SUCCESS(rc))
752 {
753 rc = RTFileRename(g_szCommsDir, InCommsSubDir(pszFilename, cchFilename), RTPATHRENAME_FLAGS_REPLACE);
754 if (RT_SUCCESS(rc) && g_uVerbosity >= 3)
755 RTMsgInfo("comms: placed '%s'\n", g_szCommsSubDir);
756 if (RT_FAILURE(rc))
757 {
758 RTMsgError("Error renaming '%s' to '%s': %Rrc", g_szCommsDir, g_szCommsSubDir, rc);
759 RTFileDelete(g_szCommsDir);
760 }
761 }
762 return rc;
763}
764
765
766/**
767 * Reads the given file from the comms subdir, ensuring that it is terminated by
768 * an EOF (0x1a) character.
769 *
770 * @returns IPRT status code.
771 * @retval VERR_TRY_AGAIN if the file is incomplete.
772 * @retval VERR_FILE_TOO_BIG if the file is considered too big.
773 * @retval VERR_FILE_NOT_FOUND if not found.
774 *
775 * @param iSeqNo The sequence number.
776 * @param pszSuffix The filename suffix.
777 * @param ppszContent Where to return the content.
778 */
779static int FsPerfCommsReadFile(uint32_t iSeqNo, const char *pszSuffix, char **ppszContent)
780{
781 *ppszContent = NULL;
782
783 RTStrPrintf(&g_szCommsSubDir[g_cchCommsSubDir], sizeof(g_szCommsSubDir) - g_cchCommsSubDir, "%u%s", iSeqNo, pszSuffix);
784 RTFILE hFile;
785 int rc = RTFileOpen(&hFile, g_szCommsSubDir, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
786 if (RT_SUCCESS(rc))
787 {
788 size_t cbUsed = 0;
789 size_t cbAlloc = 1024;
790 char *pszBuf = (char *)RTMemAllocZ(cbAlloc);
791 for (;;)
792 {
793 /* Do buffer resizing. */
794 size_t cbMaxRead = cbAlloc - cbUsed - 1;
795 if (cbMaxRead < 8)
796 {
797 if (cbAlloc < _1M)
798 {
799 cbAlloc *= 2;
800 void *pvRealloced = RTMemRealloc(pszBuf, cbAlloc);
801 if (!pvRealloced)
802 {
803 rc = VERR_NO_MEMORY;
804 break;
805 }
806 pszBuf = (char *)pvRealloced;
807 RT_BZERO(&pszBuf[cbAlloc / 2], cbAlloc);
808 cbMaxRead = cbAlloc - cbUsed - 1;
809 }
810 else
811 {
812 RTMsgError("File '%s' is too big - giving up at 1MB", g_szCommsSubDir);
813 rc = VERR_FILE_TOO_BIG;
814 break;
815 }
816 }
817
818 /* Do the reading. */
819 size_t cbActual = 0;
820 rc = RTFileRead(hFile, &pszBuf[cbUsed], cbMaxRead, &cbActual);
821 if (RT_SUCCESS(rc))
822 cbUsed += cbActual;
823 else
824 {
825 RTMsgError("Failed to read '%s': %Rrc", g_szCommsSubDir, rc);
826 break;
827 }
828
829 /* EOF? */
830 if (cbActual < cbMaxRead)
831 break;
832 }
833
834 RTFileClose(hFile);
835
836 /*
837 * Check if the file ends with the EOF marker.
838 */
839 if ( RT_SUCCESS(rc)
840 && ( cbUsed == 0
841 || pszBuf[cbUsed - 1] != FSPERF_EOF))
842 rc = VERR_TRY_AGAIN;
843
844 /*
845 * Return or free the content we've read.
846 */
847 if (RT_SUCCESS(rc))
848 *ppszContent = pszBuf;
849 else
850 RTMemFree(pszBuf);
851 }
852 else if (rc != VERR_FILE_NOT_FOUND && rc != VERR_SHARING_VIOLATION)
853 RTMsgError("Failed to open '%s': %Rrc", g_szCommsSubDir, rc);
854 return rc;
855}
856
857
858/**
859 * FsPerfCommsReadFile + renaming from the comms subdir to the comms dir.
860 *
861 * g_szCommsSubDir holds the original filename and g_szCommsDir the final
862 * filename on success.
863 */
864static int FsPerfCommsReadFileAndRename(uint32_t iSeqNo, const char *pszSuffix, const char *pszRenameSuffix, char **ppszContent)
865{
866 RTStrPrintf(&g_szCommsDir[g_cchCommsDir], sizeof(g_szCommsDir) - g_cchCommsDir, "%u%s", iSeqNo, pszRenameSuffix);
867 int rc = FsPerfCommsReadFile(iSeqNo, pszSuffix, ppszContent);
868 if (RT_SUCCESS(rc))
869 {
870 rc = RTFileRename(g_szCommsSubDir, g_szCommsDir, RTPATHRENAME_FLAGS_REPLACE);
871 if (RT_FAILURE(rc))
872 {
873 RTMsgError("Error renaming '%s' to '%s': %Rrc", g_szCommsSubDir, g_szCommsDir, rc);
874 RTMemFree(*ppszContent);
875 *ppszContent = NULL;
876 }
877 }
878 return rc;
879}
880
881
882/** The comms master sequence number. */
883static uint32_t g_iSeqNoMaster = 0;
884
885
886/**
887 * Sends a script to the remote comms slave.
888 *
889 * @returns IPRT status code giving the scripts execution status.
890 * @param pszScript The script.
891 */
892static int FsPerfCommsSend(const char *pszScript)
893{
894 /*
895 * Make sure the script is correctly terminated with an EOF control character.
896 */
897 size_t const cchScript = strlen(pszScript);
898 AssertReturn(cchScript > 0 && pszScript[cchScript - 1] == FSPERF_EOF, VERR_INVALID_PARAMETER);
899
900 /*
901 * Make sure the comms slave is running.
902 */
903 if (!RTFileExists(InCommsDir(RT_STR_TUPLE("slave.pid"))))
904 return VERR_PIPE_NOT_CONNECTED;
905
906 /*
907 * Format all the names we might want to check for.
908 */
909 char szSendNm[32];
910 size_t const cchSendNm = RTStrPrintf(szSendNm, sizeof(szSendNm), "%u-order.send", g_iSeqNoMaster);
911
912 char szAckNm[64];
913 size_t const cchAckNm = RTStrPrintf(szAckNm, sizeof(szAckNm), "%u-order.ack", g_iSeqNoMaster);
914
915 /*
916 * Produce the script file and submit it.
917 */
918 int rc = FsPerfCommsWriteFileAndRename(szSendNm, cchSendNm, pszScript, cchScript);
919 if (RT_SUCCESS(rc))
920 {
921 g_iSeqNoMaster++;
922
923 /*
924 * Wait for the result.
925 */
926 uint64_t const msTimeout = RT_MS_1MIN / 2;
927 uint64_t msStart = RTTimeMilliTS();
928 uint32_t msSleepX4 = 4;
929 for (;;)
930 {
931 /* Try read the result file: */
932 char *pszContent = NULL;
933 rc = FsPerfCommsReadFile(g_iSeqNoMaster - 1, "-order.done", &pszContent);
934 if (RT_SUCCESS(rc))
935 {
936 /* Split the result content into status code and error text: */
937 char *pszErrorText = strchr(pszContent, '\n');
938 if (pszErrorText)
939 {
940 *pszErrorText = '\0';
941 pszErrorText++;
942 }
943 else
944 {
945 char *pszEnd = strchr(pszContent, '\0');
946 Assert(pszEnd[-1] == FSPERF_EOF);
947 pszEnd[-1] = '\0';
948 }
949
950 /* Parse the status code: */
951 int32_t rcRemote = VERR_GENERAL_FAILURE;
952 rc = RTStrToInt32Full(pszContent, 0, &rcRemote);
953 if (rc != VINF_SUCCESS)
954 {
955 RTTestIFailed("FsPerfCommsSend: Failed to convert status code '%s'", pszContent);
956 rcRemote = VERR_GENERAL_FAILURE;
957 }
958
959 /* Display or return the text? */
960 if (RT_SUCCESS(rc) && g_uVerbosity >= 2)
961 RTMsgInfo("comms: order #%u: %Rrc%s%s\n",
962 g_iSeqNoMaster - 1, rcRemote, *pszErrorText ? " - " : "", pszErrorText);
963
964 RTMemFree(pszContent);
965 return rcRemote;
966 }
967
968 if (rc == VERR_TRY_AGAIN)
969 msSleepX4 = 4;
970
971 /* Check for timeout. */
972 if (RTTimeMilliTS() - msStart > msTimeout)
973 {
974 if (RT_SUCCESS(rc) && g_uVerbosity >= 2)
975 RTMsgInfo("comms: timed out waiting for order #%u'\n", g_iSeqNoMaster - 1);
976
977 rc = RTFileDelete(InCommsSubDir(szSendNm, cchSendNm));
978 if (RT_SUCCESS(rc))
979 {
980 g_iSeqNoMaster--;
981 rc = VERR_TIMEOUT;
982 }
983 else if (RTFileExists(InCommsDir(szAckNm, cchAckNm)))
984 rc = VERR_PIPE_BUSY;
985 else
986 rc = VERR_PIPE_IO_ERROR;
987 break;
988 }
989
990 /* Sleep a little while. */
991 msSleepX4++;
992 RTThreadSleep(msSleepX4 / 4);
993 }
994 }
995 return rc;
996}
997
998
999/**
1000 * Shuts down the comms slave if it exists.
1001 */
1002static void FsPerfCommsShutdownSlave(void)
1003{
1004 static bool s_fAlreadyShutdown = false;
1005 if (g_szCommsDir[0] != '\0' && !s_fAlreadyShutdown)
1006 {
1007 s_fAlreadyShutdown = true;
1008 FsPerfCommsSend("exit" FSPERF_EOF_STR);
1009
1010 g_szCommsDir[g_cchCommsDir] = '\0';
1011 int rc = RTDirRemoveRecursive(g_szCommsDir, RTDIRRMREC_F_CONTENT_AND_DIR | (g_fRelativeDir ? RTDIRRMREC_F_NO_ABS_PATH : 0));
1012 if (RT_FAILURE(rc))
1013 RTTestFailed(g_hTest, "RTDirRemoveRecursive(%s,) -> %Rrc\n", g_szCommsDir, rc);
1014 }
1015}
1016
1017
1018
1019/*********************************************************************************************************************************
1020* Comms Slave *
1021*********************************************************************************************************************************/
1022
1023typedef struct FSPERFCOMMSSLAVESTATE
1024{
1025 uint32_t iSeqNo;
1026 bool fTerminate;
1027 RTEXITCODE rcExit;
1028 RTFILE ahFiles[8];
1029 char *apszFilenames[8];
1030
1031 /** The current command. */
1032 const char *pszCommand;
1033 /** The current line number. */
1034 uint32_t iLineNo;
1035 /** The current line content. */
1036 const char *pszLine;
1037 /** Where to return extra error info text. */
1038 RTERRINFOSTATIC ErrInfo;
1039} FSPERFCOMMSSLAVESTATE;
1040
1041
1042static void FsPerfSlaveStateInit(FSPERFCOMMSSLAVESTATE *pState)
1043{
1044 pState->iSeqNo = 0;
1045 pState->fTerminate = false;
1046 pState->rcExit = RTEXITCODE_SUCCESS;
1047 unsigned i = RT_ELEMENTS(pState->ahFiles);
1048 while (i-- > 0)
1049 {
1050 pState->ahFiles[i] = NIL_RTFILE;
1051 pState->apszFilenames[i] = NULL;
1052 }
1053 RTErrInfoInitStatic(&pState->ErrInfo);
1054}
1055
1056
1057static void FsPerfSlaveStateCleanup(FSPERFCOMMSSLAVESTATE *pState)
1058{
1059 unsigned i = RT_ELEMENTS(pState->ahFiles);
1060 while (i-- > 0)
1061 {
1062 if (pState->ahFiles[i] != NIL_RTFILE)
1063 {
1064 RTFileClose(pState->ahFiles[i]);
1065 pState->ahFiles[i] = NIL_RTFILE;
1066 }
1067 if (pState->apszFilenames[i] != NULL)
1068 {
1069 RTStrFree(pState->apszFilenames[i]);
1070 pState->apszFilenames[i] = NULL;
1071 }
1072 }
1073}
1074
1075
1076/** Helper reporting a error. */
1077static int FsPerfSlaveError(FSPERFCOMMSSLAVESTATE *pState, int rc, const char *pszError, ...)
1078{
1079 va_list va;
1080 va_start(va, pszError);
1081 RTErrInfoSetF(&pState->ErrInfo.Core, VERR_PARSE_ERROR, "line %u: %s: error: %N",
1082 pState->iLineNo, pState->pszCommand, pszError, &va);
1083 va_end(va);
1084 return rc;
1085}
1086
1087
1088/** Helper reporting a syntax error. */
1089static int FsPerfSlaveSyntax(FSPERFCOMMSSLAVESTATE *pState, const char *pszError, ...)
1090{
1091 va_list va;
1092 va_start(va, pszError);
1093 RTErrInfoSetF(&pState->ErrInfo.Core, VERR_PARSE_ERROR, "line %u: %s: syntax error: %N",
1094 pState->iLineNo, pState->pszCommand, pszError, &va);
1095 va_end(va);
1096 return VERR_PARSE_ERROR;
1097}
1098
1099
1100/** Helper for parsing an unsigned 64-bit integer argument. */
1101static int FsPerfSlaveParseU64(FSPERFCOMMSSLAVESTATE *pState, const char *pszArg, const char *pszName,
1102 unsigned uBase, uint64_t uMin, uint64_t uLast, uint64_t *puValue)
1103{
1104 *puValue = uMin;
1105 uint64_t uValue;
1106 int rc = RTStrToUInt64Full(pszArg, uBase, &uValue);
1107 if (RT_FAILURE(rc))
1108 return FsPerfSlaveSyntax(pState, "invalid %s: %s (RTStrToUInt64Full -> %Rrc)", pszName, pszArg, rc);
1109 if (uValue < uMin || uValue > uLast)
1110 return FsPerfSlaveSyntax(pState, "%s is out of range: %u, valid range %u..%u", pszName, uValue, uMin, uLast);
1111 *puValue = uValue;
1112 return VINF_SUCCESS;
1113}
1114
1115
1116/** Helper for parsing an unsigned 32-bit integer argument. */
1117static int FsPerfSlaveParseU32(FSPERFCOMMSSLAVESTATE *pState, const char *pszArg, const char *pszName,
1118 unsigned uBase, uint32_t uMin, uint32_t uLast, uint32_t *puValue)
1119{
1120 *puValue = uMin;
1121 uint32_t uValue;
1122 int rc = RTStrToUInt32Full(pszArg, uBase, &uValue);
1123 if (RT_FAILURE(rc))
1124 return FsPerfSlaveSyntax(pState, "invalid %s: %s (RTStrToUInt32Full -> %Rrc)", pszName, pszArg, rc);
1125 if (uValue < uMin || uValue > uLast)
1126 return FsPerfSlaveSyntax(pState, "%s is out of range: %u, valid range %u..%u", pszName, uValue, uMin, uLast);
1127 *puValue = uValue;
1128 return VINF_SUCCESS;
1129}
1130
1131
1132/** Helper for parsing a file handle index argument. */
1133static int FsPerfSlaveParseFileIdx(FSPERFCOMMSSLAVESTATE *pState, const char *pszArg, uint32_t *pidxFile)
1134{
1135 return FsPerfSlaveParseU32(pState, pszArg, "file index", 0, 0, RT_ELEMENTS(pState->ahFiles) - 1, pidxFile);
1136}
1137
1138
1139/**
1140 * 'open {idxFile} {filename} {access} {disposition} [sharing] [mode]'
1141 */
1142static int FsPerfSlaveHandleOpen(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
1143{
1144 /*
1145 * Parse parameters.
1146 */
1147 if (cArgs > 1 + 6 || cArgs < 1 + 4)
1148 return FsPerfSlaveSyntax(pState, "takes four to six arguments, not %u", cArgs);
1149
1150 uint32_t idxFile;
1151 int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
1152 if (RT_FAILURE(rc))
1153 return rc;
1154
1155 const char *pszFilename = papszArgs[2];
1156
1157 uint64_t fOpen = 0;
1158 rc = RTFileModeToFlagsEx(papszArgs[3], papszArgs[4], papszArgs[5], &fOpen);
1159 if (RT_FAILURE(rc))
1160 return FsPerfSlaveSyntax(pState, "failed to parse access (%s), disposition (%s) and sharing (%s): %Rrc",
1161 papszArgs[3], papszArgs[4], papszArgs[5] ? papszArgs[5] : "", rc);
1162
1163 uint32_t uMode = 0660;
1164 if (cArgs >= 1 + 6)
1165 {
1166 rc = FsPerfSlaveParseU32(pState, papszArgs[6], "mode", 8, 0, 0777, &uMode);
1167 if (RT_FAILURE(rc))
1168 return rc;
1169 fOpen |= uMode << RTFILE_O_CREATE_MODE_SHIFT;
1170 }
1171
1172 /*
1173 * Is there already a file assigned to the file handle index?
1174 */
1175 if (pState->ahFiles[idxFile] != NIL_RTFILE)
1176 return FsPerfSlaveError(pState, VERR_RESOURCE_BUSY, "handle #%u is already in use for '%s'",
1177 idxFile, pState->apszFilenames[idxFile]);
1178
1179 /*
1180 * Check the filename length.
1181 */
1182 size_t const cchFilename = strlen(pszFilename);
1183 if (g_cchDir + cchFilename >= sizeof(g_szDir))
1184 return FsPerfSlaveError(pState, VERR_FILENAME_TOO_LONG, "'%.*s%s'", g_cchDir, g_szDir, pszFilename);
1185
1186 /*
1187 * Duplicate the name and execute the command.
1188 */
1189 char *pszDup = RTStrDup(pszFilename);
1190 if (!pszDup)
1191 return FsPerfSlaveError(pState, VERR_NO_STR_MEMORY, "out of memory");
1192
1193 RTFILE hFile = NIL_RTFILE;
1194 rc = RTFileOpen(&hFile, InDir(pszFilename, cchFilename), fOpen);
1195 if (RT_SUCCESS(rc))
1196 {
1197 pState->ahFiles[idxFile] = hFile;
1198 pState->apszFilenames[idxFile] = pszDup;
1199 }
1200 else
1201 {
1202 RTStrFree(pszDup);
1203 rc = FsPerfSlaveError(pState, rc, "%s: %Rrc", pszFilename, rc);
1204 }
1205 return rc;
1206}
1207
1208
1209/**
1210 * 'close {idxFile}'
1211 */
1212static int FsPerfSlaveHandleClose(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
1213{
1214 /*
1215 * Parse parameters.
1216 */
1217 if (cArgs > 1 + 1)
1218 return FsPerfSlaveSyntax(pState, "takes exactly one argument, not %u", cArgs);
1219
1220 uint32_t idxFile;
1221 int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
1222 if (RT_SUCCESS(rc))
1223 {
1224 /*
1225 * Do it.
1226 */
1227 rc = RTFileClose(pState->ahFiles[idxFile]);
1228 if (RT_SUCCESS(rc))
1229 {
1230 pState->ahFiles[idxFile] = NIL_RTFILE;
1231 RTStrFree(pState->apszFilenames[idxFile]);
1232 pState->apszFilenames[idxFile] = NULL;
1233 }
1234 }
1235 return rc;
1236}
1237
1238/** @name Patterns for 'writepattern'
1239 * @{ */
1240static uint8_t const g_abPattern0[] = { 0xf0 };
1241static uint8_t const g_abPattern1[] = { 0xf1 };
1242static uint8_t const g_abPattern2[] = { 0xf2 };
1243static uint8_t const g_abPattern3[] = { 0xf3 };
1244static uint8_t const g_abPattern4[] = { 0xf4 };
1245static uint8_t const g_abPattern5[] = { 0xf5 };
1246static uint8_t const g_abPattern6[] = { 0xf6 };
1247static uint8_t const g_abPattern7[] = { 0xf7 };
1248static uint8_t const g_abPattern8[] = { 0xf8 };
1249static uint8_t const g_abPattern9[] = { 0xf9 };
1250static uint8_t const g_abPattern10[] = { 0x1f, 0x4e, 0x99, 0xec, 0x71, 0x71, 0x48, 0x0f, 0xa7, 0x5c, 0xb4, 0x5a, 0x1f, 0xc7, 0xd0, 0x93 };
1251static struct
1252{
1253 uint8_t const *pb;
1254 uint32_t cb;
1255} const g_aPatterns[] =
1256{
1257 { g_abPattern0, sizeof(g_abPattern0) },
1258 { g_abPattern1, sizeof(g_abPattern1) },
1259 { g_abPattern2, sizeof(g_abPattern2) },
1260 { g_abPattern3, sizeof(g_abPattern3) },
1261 { g_abPattern4, sizeof(g_abPattern4) },
1262 { g_abPattern5, sizeof(g_abPattern5) },
1263 { g_abPattern6, sizeof(g_abPattern6) },
1264 { g_abPattern7, sizeof(g_abPattern7) },
1265 { g_abPattern8, sizeof(g_abPattern8) },
1266 { g_abPattern9, sizeof(g_abPattern9) },
1267 { g_abPattern10, sizeof(g_abPattern10) },
1268};
1269/** @} */
1270
1271/**
1272 * 'writepattern {idxFile} {offFile} {idxPattern} {cbToWrite}'
1273 */
1274static int FsPerfSlaveHandleWritePattern(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
1275{
1276 /*
1277 * Parse parameters.
1278 */
1279 if (cArgs > 1 + 4)
1280 return FsPerfSlaveSyntax(pState, "takes exactly four arguments, not %u", cArgs);
1281
1282 uint32_t idxFile;
1283 int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
1284 if (RT_FAILURE(rc))
1285 return rc;
1286
1287 uint64_t offFile;
1288 rc = FsPerfSlaveParseU64(pState, papszArgs[2], "file offset", 0, 0, UINT64_MAX / 4, &offFile);
1289 if (RT_FAILURE(rc))
1290 return rc;
1291
1292 uint32_t idxPattern;
1293 rc = FsPerfSlaveParseU32(pState, papszArgs[3], "pattern index", 0, 0, RT_ELEMENTS(g_aPatterns) - 1, &idxPattern);
1294 if (RT_FAILURE(rc))
1295 return rc;
1296
1297 uint64_t cbToWrite;
1298 rc = FsPerfSlaveParseU64(pState, papszArgs[4], "number of bytes to write", 0, 0, _1G, &cbToWrite);
1299 if (RT_FAILURE(rc))
1300 return rc;
1301
1302 if (pState->ahFiles[idxFile] == NIL_RTFILE)
1303 return FsPerfSlaveError(pState, VERR_INVALID_HANDLE, "no open file at index #%u", idxFile);
1304
1305 /*
1306 * Allocate a suitable buffer.
1307 */
1308 size_t cbBuf = cbToWrite >= _2M ? _2M : RT_ALIGN_Z((size_t)cbToWrite, 512);
1309 uint8_t *pbBuf = (uint8_t *)RTMemTmpAlloc(cbBuf);
1310 if (!pbBuf)
1311 {
1312 cbBuf = _4K;
1313 pbBuf = (uint8_t *)RTMemTmpAlloc(cbBuf);
1314 if (!pbBuf)
1315 return FsPerfSlaveError(pState, VERR_NO_TMP_MEMORY, "failed to allocate 4KB for buffers");
1316 }
1317
1318 /*
1319 * Fill 1 byte patterns before we start looping.
1320 */
1321 if (g_aPatterns[idxPattern].cb == 1)
1322 memset(pbBuf, g_aPatterns[idxPattern].pb[0], cbBuf);
1323
1324 /*
1325 * The write loop.
1326 */
1327 uint32_t offPattern = 0;
1328 while (cbToWrite > 0)
1329 {
1330 /*
1331 * Fill the buffer if multi-byte pattern (single byte patterns are handled before the loop):
1332 */
1333 if (g_aPatterns[idxPattern].cb > 1)
1334 {
1335 uint32_t const cbSrc = g_aPatterns[idxPattern].cb;
1336 uint8_t const * const pbSrc = g_aPatterns[idxPattern].pb;
1337 size_t cbDst = cbBuf;
1338 uint8_t *pbDst = pbBuf;
1339
1340 /* first iteration, potential partial pattern. */
1341 if (offPattern >= cbSrc)
1342 offPattern = 0;
1343 size_t cbThis1 = RT_MIN(g_aPatterns[idxPattern].cb - offPattern, cbToWrite);
1344 memcpy(pbDst, &pbSrc[offPattern], cbThis1);
1345 cbDst -= cbThis1;
1346 if (cbDst > 0)
1347 {
1348 pbDst += cbThis1;
1349 offPattern = 0;
1350
1351 /* full patterns */
1352 while (cbDst >= cbSrc)
1353 {
1354 memcpy(pbDst, pbSrc, cbSrc);
1355 pbDst += cbSrc;
1356 cbDst -= cbSrc;
1357 }
1358
1359 /* partial final copy */
1360 if (cbDst > 0)
1361 {
1362 memcpy(pbDst, pbSrc, cbDst);
1363 offPattern = (uint32_t)cbDst;
1364 }
1365 }
1366 }
1367
1368 /*
1369 * Write.
1370 */
1371 size_t const cbThisWrite = (size_t)RT_MIN(cbToWrite, cbBuf);
1372 rc = RTFileWriteAt(pState->ahFiles[idxFile], offFile, pbBuf, cbThisWrite, NULL);
1373 if (RT_FAILURE(rc))
1374 {
1375 FsPerfSlaveError(pState, rc, "error writing %#zx bytes at %#RX64: %Rrc (file: %s)",
1376 cbThisWrite, offFile, rc, pState->apszFilenames[idxFile]);
1377 break;
1378 }
1379
1380 offFile += cbThisWrite;
1381 cbToWrite -= cbThisWrite;
1382 }
1383
1384 RTMemTmpFree(pbBuf);
1385 return rc;
1386}
1387
1388
1389/**
1390 * 'truncate {idxFile} {cbFile}'
1391 */
1392static int FsPerfSlaveHandleTruncate(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
1393{
1394 /*
1395 * Parse parameters.
1396 */
1397 if (cArgs != 1 + 2)
1398 return FsPerfSlaveSyntax(pState, "takes exactly two arguments, not %u", cArgs);
1399
1400 uint32_t idxFile;
1401 int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
1402 if (RT_FAILURE(rc))
1403 return rc;
1404
1405 uint64_t cbFile;
1406 rc = FsPerfSlaveParseU64(pState, papszArgs[2], "new file size", 0, 0, UINT64_MAX / 4, &cbFile);
1407 if (RT_FAILURE(rc))
1408 return rc;
1409
1410 if (pState->ahFiles[idxFile] == NIL_RTFILE)
1411 return FsPerfSlaveError(pState, VERR_INVALID_HANDLE, "no open file at index #%u", idxFile);
1412
1413 /*
1414 * Execute.
1415 */
1416 rc = RTFileSetSize(pState->ahFiles[idxFile], cbFile);
1417 if (RT_FAILURE(rc))
1418 return FsPerfSlaveError(pState, rc, "failed to set file size to %#RX64: %Rrc (file: %s)",
1419 cbFile, rc, pState->apszFilenames[idxFile]);
1420 return VINF_SUCCESS;
1421}
1422
1423
1424/**
1425 * 'futimes {idxFile} {modified|0} [access|0] [change|0] [birth|0]'
1426 */
1427static int FsPerfSlaveHandleFUTimes(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
1428{
1429 /*
1430 * Parse parameters.
1431 */
1432 if (cArgs < 1 + 2 || cArgs > 1 + 5)
1433 return FsPerfSlaveSyntax(pState, "takes between two and five arguments, not %u", cArgs);
1434
1435 uint32_t idxFile;
1436 int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
1437 if (RT_FAILURE(rc))
1438 return rc;
1439
1440 uint64_t nsModifiedTime;
1441 rc = FsPerfSlaveParseU64(pState, papszArgs[2], "modified time", 0, 0, UINT64_MAX, &nsModifiedTime);
1442 if (RT_FAILURE(rc))
1443 return rc;
1444
1445 uint64_t nsAccessTime = 0;
1446 if (cArgs >= 1 + 3)
1447 {
1448 rc = FsPerfSlaveParseU64(pState, papszArgs[3], "access time", 0, 0, UINT64_MAX, &nsAccessTime);
1449 if (RT_FAILURE(rc))
1450 return rc;
1451 }
1452
1453 uint64_t nsChangeTime = 0;
1454 if (cArgs >= 1 + 4)
1455 {
1456 rc = FsPerfSlaveParseU64(pState, papszArgs[4], "change time", 0, 0, UINT64_MAX, &nsChangeTime);
1457 if (RT_FAILURE(rc))
1458 return rc;
1459 }
1460
1461 uint64_t nsBirthTime = 0;
1462 if (cArgs >= 1 + 5)
1463 {
1464 rc = FsPerfSlaveParseU64(pState, papszArgs[4], "birth time", 0, 0, UINT64_MAX, &nsBirthTime);
1465 if (RT_FAILURE(rc))
1466 return rc;
1467 }
1468
1469 if (pState->ahFiles[idxFile] == NIL_RTFILE)
1470 return FsPerfSlaveError(pState, VERR_INVALID_HANDLE, "no open file at index #%u", idxFile);
1471
1472 /*
1473 * Execute.
1474 */
1475 RTTIMESPEC ModifiedTime;
1476 RTTIMESPEC AccessTime;
1477 RTTIMESPEC ChangeTime;
1478 RTTIMESPEC BirthTime;
1479 rc = RTFileSetTimes(pState->ahFiles[idxFile],
1480 nsAccessTime ? RTTimeSpecSetNano(&AccessTime, nsAccessTime) : NULL,
1481 nsModifiedTime ? RTTimeSpecSetNano(&ModifiedTime, nsModifiedTime) : NULL,
1482 nsChangeTime ? RTTimeSpecSetNano(&ChangeTime, nsChangeTime) : NULL,
1483 nsBirthTime ? RTTimeSpecSetNano(&BirthTime, nsBirthTime) : NULL);
1484 if (RT_FAILURE(rc))
1485 return FsPerfSlaveError(pState, rc, "failed to set file times to %RI64, %RI64, %RI64, %RI64: %Rrc (file: %s)",
1486 nsModifiedTime, nsAccessTime, nsChangeTime, nsBirthTime, rc, pState->apszFilenames[idxFile]);
1487 return VINF_SUCCESS;
1488}
1489
1490
1491/**
1492 * 'fchmod {idxFile} {cbFile}'
1493 */
1494static int FsPerfSlaveHandleFChMod(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
1495{
1496 /*
1497 * Parse parameters.
1498 */
1499 if (cArgs != 1 + 2)
1500 return FsPerfSlaveSyntax(pState, "takes exactly two arguments, not %u", cArgs);
1501
1502 uint32_t idxFile;
1503 int rc = FsPerfSlaveParseFileIdx(pState, papszArgs[1], &idxFile);
1504 if (RT_FAILURE(rc))
1505 return rc;
1506
1507 uint32_t fAttribs;
1508 rc = FsPerfSlaveParseU32(pState, papszArgs[2], "new file attributes", 0, 0, UINT32_MAX, &fAttribs);
1509 if (RT_FAILURE(rc))
1510 return rc;
1511
1512 if (pState->ahFiles[idxFile] == NIL_RTFILE)
1513 return FsPerfSlaveError(pState, VERR_INVALID_HANDLE, "no open file at index #%u", idxFile);
1514
1515 /*
1516 * Execute.
1517 */
1518 rc = RTFileSetMode(pState->ahFiles[idxFile], fAttribs);
1519 if (RT_FAILURE(rc))
1520 return FsPerfSlaveError(pState, rc, "failed to set file mode to %#RX32: %Rrc (file: %s)",
1521 fAttribs, rc, pState->apszFilenames[idxFile]);
1522 return VINF_SUCCESS;
1523}
1524
1525
1526/**
1527 * 'reset'
1528 */
1529static int FsPerfSlaveHandleReset(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
1530{
1531 /*
1532 * Parse parameters.
1533 */
1534 if (cArgs > 1)
1535 return FsPerfSlaveSyntax(pState, "takes zero arguments, not %u", cArgs);
1536 RT_NOREF(papszArgs);
1537
1538 /*
1539 * Execute the command.
1540 */
1541 FsPerfSlaveStateCleanup(pState);
1542 return VINF_SUCCESS;
1543}
1544
1545
1546/**
1547 * 'exit [exitcode]'
1548 */
1549static int FsPerfSlaveHandleExit(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs)
1550{
1551 /*
1552 * Parse parameters.
1553 */
1554 if (cArgs > 1 + 1)
1555 return FsPerfSlaveSyntax(pState, "takes zero or one argument, not %u", cArgs);
1556
1557 if (cArgs >= 1 + 1)
1558 {
1559 uint32_t uExitCode;
1560 int rc = FsPerfSlaveParseU32(pState, papszArgs[1], "exit code", 0, 0, 127, &uExitCode);
1561 if (RT_FAILURE(rc))
1562 return rc;
1563
1564 /*
1565 * Execute the command.
1566 */
1567 pState->rcExit = (RTEXITCODE)uExitCode;
1568 }
1569 pState->fTerminate = true;
1570 return VINF_SUCCESS;
1571}
1572
1573
1574/**
1575 * Executes a script line.
1576 */
1577static int FsPerfSlaveExecuteLine(FSPERFCOMMSSLAVESTATE *pState, char *pszLine)
1578{
1579 /*
1580 * Parse the command line using bourne shell quoting style.
1581 */
1582 char **papszArgs;
1583 int cArgs;
1584 int rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszLine, RTGETOPTARGV_CNV_QUOTE_BOURNE_SH, NULL);
1585 if (RT_FAILURE(rc))
1586 return RTErrInfoSetF(&pState->ErrInfo.Core, rc, "Failed to parse line %u: %s", pState->iLineNo, pszLine);
1587 if (cArgs <= 0)
1588 {
1589 RTGetOptArgvFree(papszArgs);
1590 return RTErrInfoSetF(&pState->ErrInfo.Core, rc, "No command found on line %u: %s", pState->iLineNo, pszLine);
1591 }
1592
1593 /*
1594 * Execute the command.
1595 */
1596 static const struct
1597 {
1598 const char *pszCmd;
1599 size_t cchCmd;
1600 int (*pfnHandler)(FSPERFCOMMSSLAVESTATE *pState, char **papszArgs, int cArgs);
1601 } s_aHandlers[] =
1602 {
1603 { RT_STR_TUPLE("open"), FsPerfSlaveHandleOpen },
1604 { RT_STR_TUPLE("close"), FsPerfSlaveHandleClose },
1605 { RT_STR_TUPLE("writepattern"), FsPerfSlaveHandleWritePattern },
1606 { RT_STR_TUPLE("truncate"), FsPerfSlaveHandleTruncate },
1607 { RT_STR_TUPLE("futimes"), FsPerfSlaveHandleFUTimes},
1608 { RT_STR_TUPLE("fchmod"), FsPerfSlaveHandleFChMod },
1609 { RT_STR_TUPLE("reset"), FsPerfSlaveHandleReset },
1610 { RT_STR_TUPLE("exit"), FsPerfSlaveHandleExit },
1611 };
1612 const char * const pszCmd = papszArgs[0];
1613 size_t const cchCmd = strlen(pszCmd);
1614 for (size_t i = 0; i < RT_ELEMENTS(s_aHandlers); i++)
1615 if ( s_aHandlers[i].cchCmd == cchCmd
1616 && memcmp(pszCmd, s_aHandlers[i].pszCmd, cchCmd) == 0)
1617 {
1618 pState->pszCommand = s_aHandlers[i].pszCmd;
1619 rc = s_aHandlers[i].pfnHandler(pState, papszArgs, cArgs);
1620 RTGetOptArgvFree(papszArgs);
1621 return rc;
1622 }
1623
1624 rc = RTErrInfoSetF(&pState->ErrInfo.Core, VERR_NOT_FOUND, "Command on line %u not found: %s", pState->iLineNo, pszLine);
1625 RTGetOptArgvFree(papszArgs);
1626 return rc;
1627}
1628
1629
1630/**
1631 * Executes a script.
1632 */
1633static int FsPerfSlaveExecuteScript(FSPERFCOMMSSLAVESTATE *pState, char *pszContent)
1634{
1635 /*
1636 * Validate the encoding.
1637 */
1638 int rc = RTStrValidateEncoding(pszContent);
1639 if (RT_FAILURE(rc))
1640 return RTErrInfoSetF(&pState->ErrInfo.Core, rc, "Invalid UTF-8 encoding");
1641
1642 /*
1643 * Work the script content line by line.
1644 */
1645 pState->iLineNo = 0;
1646 while (*pszContent != FSPERF_EOF && *pszContent != '\0')
1647 {
1648 pState->iLineNo++;
1649
1650 /* Figure the current line and move pszContent ahead: */
1651 char *pszLine = RTStrStripL(pszContent);
1652 char *pszEol = strchr(pszLine, '\n');
1653 if (pszEol)
1654 pszContent = pszEol + 1;
1655 else
1656 {
1657 pszEol = strchr(pszLine, FSPERF_EOF);
1658 AssertStmt(pszEol, pszEol = strchr(pszLine, '\0'));
1659 pszContent = pszEol;
1660 }
1661
1662 /* Terminate and strip it: */
1663 *pszEol = '\0';
1664 pszLine = RTStrStrip(pszLine);
1665
1666 /* Skip empty lines and comment lines: */
1667 if (*pszLine == '\0' || *pszLine == '#')
1668 continue;
1669
1670 /* Execute the line: */
1671 pState->pszLine = pszLine;
1672 rc = FsPerfSlaveExecuteLine(pState, pszLine);
1673 if (RT_FAILURE(rc))
1674 break;
1675 }
1676 return rc;
1677}
1678
1679
1680/**
1681 * Communication slave.
1682 *
1683 * @returns exit code.
1684 */
1685static int FsPerfCommsSlave(void)
1686{
1687 /*
1688 * Make sure we've got a directory and create it and it's subdir.
1689 */
1690 if (g_cchCommsDir == 0)
1691 return RTMsgError("no communcation directory was specified (-C)");
1692
1693 int rc = RTDirCreateFullPath(g_szCommsSubDir, 0775);
1694 if (RT_FAILURE(rc))
1695 return RTMsgError("Failed to create '%s': %Rrc", g_szCommsSubDir, rc);
1696
1697 /*
1698 * Signal that we're here.
1699 */
1700 char szTmp[_4K];
1701 rc = FsPerfCommsWriteFile(RT_STR_TUPLE("slave.pid"), szTmp, RTStrPrintf(szTmp, sizeof(szTmp),
1702 "%u" FSPERF_EOF_STR, RTProcSelf()));
1703 if (RT_FAILURE(rc))
1704 return RTEXITCODE_FAILURE;
1705
1706 /*
1707 * Processing loop.
1708 */
1709 FSPERFCOMMSSLAVESTATE State;
1710 FsPerfSlaveStateInit(&State);
1711 uint32_t msSleep = 1;
1712 while (!State.fTerminate)
1713 {
1714 /*
1715 * Try read the next command script.
1716 */
1717 char *pszContent = NULL;
1718 rc = FsPerfCommsReadFileAndRename(State.iSeqNo, "-order.send", "-order.ack", &pszContent);
1719 if (RT_SUCCESS(rc))
1720 {
1721 /*
1722 * Execute it.
1723 */
1724 RTErrInfoInitStatic(&State.ErrInfo);
1725 rc = FsPerfSlaveExecuteScript(&State, pszContent);
1726
1727 /*
1728 * Write the result.
1729 */
1730 char szResult[64];
1731 size_t cchResult = RTStrPrintf(szResult, sizeof(szResult), "%u-order.done", State.iSeqNo);
1732 size_t cchTmp = RTStrPrintf(szTmp, sizeof(szTmp), "%d\n%s" FSPERF_EOF_STR,
1733 rc, RTErrInfoIsSet(&State.ErrInfo.Core) ? State.ErrInfo.Core.pszMsg : "");
1734 FsPerfCommsWriteFileAndRename(szResult, cchResult, szTmp, cchTmp);
1735 State.iSeqNo++;
1736
1737 msSleep = 1;
1738 }
1739
1740 /*
1741 * Wait a little and check again.
1742 */
1743 RTThreadSleep(msSleep);
1744 if (msSleep < 128)
1745 msSleep++;
1746 }
1747
1748 /*
1749 * Remove the we're here indicator and quit.
1750 */
1751 RTFileDelete(InCommsDir(RT_STR_TUPLE("slave.pid")));
1752 FsPerfSlaveStateCleanup(&State);
1753 return State.rcExit;
1754}
1755
1756
1757
1758/*********************************************************************************************************************************
1759* Tests *
1760*********************************************************************************************************************************/
1761
1762/**
1763 * Prepares the test area.
1764 * @returns VBox status code.
1765 */
1766static int fsPrepTestArea(void)
1767{
1768 /* The empty subdir and associated globals: */
1769 static char s_szEmpty[] = "empty";
1770 memcpy(g_szEmptyDir, g_szDir, g_cchDir);
1771 memcpy(&g_szEmptyDir[g_cchDir], s_szEmpty, sizeof(s_szEmpty));
1772 g_cchEmptyDir = g_cchDir + sizeof(s_szEmpty) - 1;
1773 RTTESTI_CHECK_RC_RET(RTDirCreate(g_szEmptyDir, 0755, 0), VINF_SUCCESS, rcCheck);
1774 g_szEmptyDir[g_cchEmptyDir++] = RTPATH_SLASH;
1775 g_szEmptyDir[g_cchEmptyDir] = '\0';
1776 RTTestIPrintf(RTTESTLVL_ALWAYS, "Empty dir: %s\n", g_szEmptyDir);
1777
1778 /* Deep directory: */
1779 memcpy(g_szDeepDir, g_szDir, g_cchDir);
1780 g_cchDeepDir = g_cchDir;
1781 do
1782 {
1783 static char const s_szSub[] = "d" RTPATH_SLASH_STR;
1784 memcpy(&g_szDeepDir[g_cchDeepDir], s_szSub, sizeof(s_szSub));
1785 g_cchDeepDir += sizeof(s_szSub) - 1;
1786 RTTESTI_CHECK_RC_RET( RTDirCreate(g_szDeepDir, 0755, 0), VINF_SUCCESS, rcCheck);
1787 } while (g_cchDeepDir < 176);
1788 RTTestIPrintf(RTTESTLVL_ALWAYS, "Deep dir: %s\n", g_szDeepDir);
1789
1790 /* Create known file in both deep and shallow dirs: */
1791 RTFILE hKnownFile;
1792 RTTESTI_CHECK_RC_RET(RTFileOpen(&hKnownFile, InDir(RT_STR_TUPLE("known-file")),
1793 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE),
1794 VINF_SUCCESS, rcCheck);
1795 RTTESTI_CHECK_RC_RET(RTFileClose(hKnownFile), VINF_SUCCESS, rcCheck);
1796
1797 RTTESTI_CHECK_RC_RET(RTFileOpen(&hKnownFile, InDeepDir(RT_STR_TUPLE("known-file")),
1798 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE),
1799 VINF_SUCCESS, rcCheck);
1800 RTTESTI_CHECK_RC_RET(RTFileClose(hKnownFile), VINF_SUCCESS, rcCheck);
1801
1802 return VINF_SUCCESS;
1803}
1804
1805
1806/**
1807 * Create a name list entry.
1808 * @returns Pointer to the entry, NULL if out of memory.
1809 * @param pchName The name.
1810 * @param cchName The name length.
1811 */
1812PFSPERFNAMEENTRY fsPerfCreateNameEntry(const char *pchName, size_t cchName)
1813{
1814 PFSPERFNAMEENTRY pEntry = (PFSPERFNAMEENTRY)RTMemAllocVar(RT_UOFFSETOF_DYN(FSPERFNAMEENTRY, szName[cchName + 1]));
1815 if (pEntry)
1816 {
1817 RTListInit(&pEntry->Entry);
1818 pEntry->cchName = (uint16_t)cchName;
1819 memcpy(pEntry->szName, pchName, cchName);
1820 pEntry->szName[cchName] = '\0';
1821 }
1822 return pEntry;
1823}
1824
1825
1826static int fsPerfManyTreeRecursiveDirCreator(size_t cchDir, uint32_t iDepth)
1827{
1828 PFSPERFNAMEENTRY pEntry = fsPerfCreateNameEntry(g_szDir, cchDir);
1829 RTTESTI_CHECK_RET(pEntry, VERR_NO_MEMORY);
1830 RTListAppend(&g_ManyTreeHead, &pEntry->Entry);
1831
1832 RTTESTI_CHECK_RC_RET(RTDirCreate(g_szDir, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL),
1833 VINF_SUCCESS, rcCheck);
1834
1835 if (iDepth < g_cManyTreeDepth)
1836 for (uint32_t i = 0; i < g_cManyTreeSubdirsPerDir; i++)
1837 {
1838 size_t cchSubDir = RTStrPrintf(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, "d%02u" RTPATH_SLASH_STR, i);
1839 RTTESTI_CHECK_RC_RET(fsPerfManyTreeRecursiveDirCreator(cchDir + cchSubDir, iDepth + 1), VINF_SUCCESS, rcCheck);
1840 }
1841
1842 return VINF_SUCCESS;
1843}
1844
1845
1846void fsPerfManyFiles(void)
1847{
1848 RTTestISub("manyfiles");
1849
1850 /*
1851 * Create a sub-directory with like 10000 files in it.
1852 *
1853 * This does push the directory organization of the underlying file system,
1854 * which is something we might not want to profile with shared folders. It
1855 * is however useful for directory enumeration.
1856 */
1857 RTTESTI_CHECK_RC_RETV(RTDirCreate(InDir(RT_STR_TUPLE("manyfiles")), 0755,
1858 RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL),
1859 VINF_SUCCESS);
1860
1861 size_t offFilename = strlen(g_szDir);
1862 g_szDir[offFilename++] = RTPATH_SLASH;
1863
1864 fsPerfYield();
1865 RTFILE hFile;
1866 uint64_t const nsStart = RTTimeNanoTS();
1867 for (uint32_t i = 0; i < g_cManyFiles; i++)
1868 {
1869 RTStrFormatU32(&g_szDir[offFilename], sizeof(g_szDir) - offFilename, i, 10, 5, 5, RTSTR_F_ZEROPAD);
1870 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile, g_szDir, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
1871 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
1872 }
1873 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
1874 RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Creating %u empty files in single directory", g_cManyFiles);
1875 RTTestIValueF(cNsElapsed / g_cManyFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Create empty file (single dir)");
1876
1877 /*
1878 * Create a bunch of directories with exacly 32 files in each, hoping to
1879 * avoid any directory organization artifacts.
1880 */
1881 /* Create the directories first, building a list of them for simplifying iteration: */
1882 RTListInit(&g_ManyTreeHead);
1883 InDir(RT_STR_TUPLE("manytree" RTPATH_SLASH_STR));
1884 RTTESTI_CHECK_RC_RETV(fsPerfManyTreeRecursiveDirCreator(strlen(g_szDir), 0), VINF_SUCCESS);
1885
1886 /* Create the zero byte files: */
1887 fsPerfYield();
1888 uint64_t const nsStart2 = RTTimeNanoTS();
1889 uint32_t cFiles = 0;
1890 PFSPERFNAMEENTRY pCur;
1891 RTListForEach(&g_ManyTreeHead, pCur, FSPERFNAMEENTRY, Entry)
1892 {
1893 char szPath[FSPERF_MAX_PATH];
1894 memcpy(szPath, pCur->szName, pCur->cchName);
1895 for (uint32_t i = 0; i < g_cManyTreeFilesPerDir; i++)
1896 {
1897 RTStrFormatU32(&szPath[pCur->cchName], sizeof(szPath) - pCur->cchName, i, 10, 5, 5, RTSTR_F_ZEROPAD);
1898 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile, szPath, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
1899 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
1900 cFiles++;
1901 }
1902 }
1903 uint64_t const cNsElapsed2 = RTTimeNanoTS() - nsStart2;
1904 RTTestIValueF(cNsElapsed2, RTTESTUNIT_NS, "Creating %u empty files in tree", cFiles);
1905 RTTestIValueF(cNsElapsed2 / cFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Create empty file (tree)");
1906 RTTESTI_CHECK(g_cManyTreeFiles == cFiles);
1907}
1908
1909
1910DECL_FORCE_INLINE(int) fsPerfOpenExistingOnceReadonly(const char *pszFile)
1911{
1912 RTFILE hFile;
1913 RTTESTI_CHECK_RC_RET(RTFileOpen(&hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS, rcCheck);
1914 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
1915 return VINF_SUCCESS;
1916}
1917
1918
1919DECL_FORCE_INLINE(int) fsPerfOpenExistingOnceWriteonly(const char *pszFile)
1920{
1921 RTFILE hFile;
1922 RTTESTI_CHECK_RC_RET(RTFileOpen(&hFile, pszFile, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS, rcCheck);
1923 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
1924 return VINF_SUCCESS;
1925}
1926
1927
1928/** @note tstRTFileOpenEx-1.cpp has a copy of this code. */
1929static void tstOpenExTest(unsigned uLine, int cbExist, int cbNext, const char *pszFilename, uint64_t fAction,
1930 int rcExpect, RTFILEACTION enmActionExpected)
1931{
1932 uint64_t const fCreateMode = (0644 << RTFILE_O_CREATE_MODE_SHIFT);
1933 RTFILE hFile;
1934 int rc;
1935
1936 /*
1937 * File existence and size.
1938 */
1939 bool fOkay = false;
1940 RTFSOBJINFO ObjInfo;
1941 rc = RTPathQueryInfoEx(pszFilename, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1942 if (RT_SUCCESS(rc))
1943 fOkay = cbExist == (int64_t)ObjInfo.cbObject;
1944 else
1945 fOkay = rc == VERR_FILE_NOT_FOUND && cbExist < 0;
1946 if (!fOkay)
1947 {
1948 if (cbExist >= 0)
1949 {
1950 rc = RTFileOpen(&hFile, pszFilename, RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | fCreateMode);
1951 if (RT_SUCCESS(rc))
1952 {
1953 while (cbExist > 0)
1954 {
1955 int cbToWrite = (int)strlen(pszFilename);
1956 if (cbToWrite > cbExist)
1957 cbToWrite = cbExist;
1958 rc = RTFileWrite(hFile, pszFilename, cbToWrite, NULL);
1959 if (RT_FAILURE(rc))
1960 {
1961 RTTestIFailed("%u: RTFileWrite(%s,%#x) -> %Rrc\n", uLine, pszFilename, cbToWrite, rc);
1962 break;
1963 }
1964 cbExist -= cbToWrite;
1965 }
1966
1967 RTTESTI_CHECK_RC(RTFileClose(hFile), VINF_SUCCESS);
1968 }
1969 else
1970 RTTestIFailed("%u: RTFileDelete(%s) -> %Rrc\n", uLine, pszFilename, rc);
1971
1972 }
1973 else
1974 {
1975 rc = RTFileDelete(pszFilename);
1976 if (rc != VINF_SUCCESS && rc != VERR_FILE_NOT_FOUND)
1977 RTTestIFailed("%u: RTFileDelete(%s) -> %Rrc\n", uLine, pszFilename, rc);
1978 }
1979 }
1980
1981 /*
1982 * The actual test.
1983 */
1984 RTFILEACTION enmActuallyTaken = RTFILEACTION_END;
1985 hFile = NIL_RTFILE;
1986 rc = RTFileOpenEx(pszFilename, fAction | RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | fCreateMode, &hFile, &enmActuallyTaken);
1987 if ( rc != rcExpect
1988 || enmActuallyTaken != enmActionExpected
1989 || (RT_SUCCESS(rc) ? hFile == NIL_RTFILE : hFile != NIL_RTFILE))
1990 RTTestIFailed("%u: RTFileOpenEx(%s, %#llx) -> %Rrc + %d (hFile=%p), expected %Rrc + %d\n",
1991 uLine, pszFilename, fAction, rc, enmActuallyTaken, hFile, rcExpect, enmActionExpected);
1992 if (RT_SUCCESS(rc))
1993 {
1994 if ( enmActionExpected == RTFILEACTION_REPLACED
1995 || enmActionExpected == RTFILEACTION_TRUNCATED)
1996 {
1997 uint8_t abBuf[16];
1998 rc = RTFileRead(hFile, abBuf, 1, NULL);
1999 if (rc != VERR_EOF)
2000 RTTestIFailed("%u: RTFileRead(%s,,1,) -> %Rrc, expected VERR_EOF\n", uLine, pszFilename, rc);
2001 }
2002
2003 while (cbNext > 0)
2004 {
2005 int cbToWrite = (int)strlen(pszFilename);
2006 if (cbToWrite > cbNext)
2007 cbToWrite = cbNext;
2008 rc = RTFileWrite(hFile, pszFilename, cbToWrite, NULL);
2009 if (RT_FAILURE(rc))
2010 {
2011 RTTestIFailed("%u: RTFileWrite(%s,%#x) -> %Rrc\n", uLine, pszFilename, cbToWrite, rc);
2012 break;
2013 }
2014 cbNext -= cbToWrite;
2015 }
2016
2017 rc = RTFileClose(hFile);
2018 if (RT_FAILURE(rc))
2019 RTTestIFailed("%u: RTFileClose(%p) -> %Rrc\n", uLine, hFile, rc);
2020 }
2021}
2022
2023
2024void fsPerfOpen(void)
2025{
2026 RTTestISub("open");
2027
2028 /* Opening non-existing files. */
2029 RTFILE hFile;
2030 RTTESTI_CHECK_RC(RTFileOpen(&hFile, InEmptyDir(RT_STR_TUPLE("no-such-file")),
2031 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VERR_FILE_NOT_FOUND);
2032 RTTESTI_CHECK_RC(RTFileOpen(&hFile, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
2033 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), FSPERF_VERR_PATH_NOT_FOUND);
2034 RTTESTI_CHECK_RC(RTFileOpen(&hFile, InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
2035 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VERR_PATH_NOT_FOUND);
2036
2037 /*
2038 * The following is copied from tstRTFileOpenEx-1.cpp:
2039 */
2040 InDir(RT_STR_TUPLE("file1"));
2041 tstOpenExTest(__LINE__, -1, -1, g_szDir, RTFILE_O_OPEN, VERR_FILE_NOT_FOUND, RTFILEACTION_INVALID);
2042 tstOpenExTest(__LINE__, -1, -1, g_szDir, RTFILE_O_OPEN_CREATE, VINF_SUCCESS, RTFILEACTION_CREATED);
2043 tstOpenExTest(__LINE__, 0, 0, g_szDir, RTFILE_O_OPEN_CREATE, VINF_SUCCESS, RTFILEACTION_OPENED);
2044 tstOpenExTest(__LINE__, 0, 0, g_szDir, RTFILE_O_OPEN, VINF_SUCCESS, RTFILEACTION_OPENED);
2045
2046 tstOpenExTest(__LINE__, 0, 0, g_szDir, RTFILE_O_OPEN | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_TRUNCATED);
2047 tstOpenExTest(__LINE__, 0, 10, g_szDir, RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_TRUNCATED);
2048 tstOpenExTest(__LINE__, 10, 10, g_szDir, RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_TRUNCATED);
2049 tstOpenExTest(__LINE__, 10, -1, g_szDir, RTFILE_O_OPEN | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_TRUNCATED);
2050 tstOpenExTest(__LINE__, -1, -1, g_szDir, RTFILE_O_OPEN | RTFILE_O_TRUNCATE, VERR_FILE_NOT_FOUND, RTFILEACTION_INVALID);
2051 tstOpenExTest(__LINE__, -1, 0, g_szDir, RTFILE_O_OPEN_CREATE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_CREATED);
2052
2053 tstOpenExTest(__LINE__, 0, -1, g_szDir, RTFILE_O_CREATE_REPLACE, VINF_SUCCESS, RTFILEACTION_REPLACED);
2054 tstOpenExTest(__LINE__, -1, 0, g_szDir, RTFILE_O_CREATE_REPLACE, VINF_SUCCESS, RTFILEACTION_CREATED);
2055 tstOpenExTest(__LINE__, 0, -1, g_szDir, RTFILE_O_CREATE, VERR_ALREADY_EXISTS, RTFILEACTION_ALREADY_EXISTS);
2056 tstOpenExTest(__LINE__, -1, -1, g_szDir, RTFILE_O_CREATE, VINF_SUCCESS, RTFILEACTION_CREATED);
2057
2058 tstOpenExTest(__LINE__, -1, 10, g_szDir, RTFILE_O_CREATE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_CREATED);
2059 tstOpenExTest(__LINE__, 10, 10, g_szDir, RTFILE_O_CREATE | RTFILE_O_TRUNCATE, VERR_ALREADY_EXISTS, RTFILEACTION_ALREADY_EXISTS);
2060 tstOpenExTest(__LINE__, 10, -1, g_szDir, RTFILE_O_CREATE_REPLACE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_REPLACED);
2061 tstOpenExTest(__LINE__, -1, -1, g_szDir, RTFILE_O_CREATE_REPLACE | RTFILE_O_TRUNCATE, VINF_SUCCESS, RTFILEACTION_CREATED);
2062
2063 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
2064
2065 /*
2066 * Create file1 and then try exclusivly creating it again.
2067 * Then profile opening it for reading.
2068 */
2069 RTFILE hFile1;
2070 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file1")),
2071 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2072 RTTESTI_CHECK_RC(RTFileOpen(&hFile, g_szDir, RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VERR_ALREADY_EXISTS);
2073 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2074
2075 PROFILE_FN(fsPerfOpenExistingOnceReadonly(g_szDir), g_nsTestRun, "RTFileOpen/Close/Readonly");
2076 PROFILE_FN(fsPerfOpenExistingOnceWriteonly(g_szDir), g_nsTestRun, "RTFileOpen/Close/Writeonly");
2077
2078 /*
2079 * Profile opening in the deep directory too.
2080 */
2081 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file1")),
2082 RTFILE_O_CREATE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2083 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2084 PROFILE_FN(fsPerfOpenExistingOnceReadonly(g_szDeepDir), g_nsTestRun, "RTFileOpen/Close/deep/readonly");
2085 PROFILE_FN(fsPerfOpenExistingOnceWriteonly(g_szDeepDir), g_nsTestRun, "RTFileOpen/Close/deep/writeonly");
2086
2087 /* Manytree: */
2088 char szPath[FSPERF_MAX_PATH];
2089 PROFILE_MANYTREE_FN(szPath, fsPerfOpenExistingOnceReadonly(szPath), 1, g_nsTestRun, "RTFileOpen/Close/manytree/readonly");
2090}
2091
2092
2093void fsPerfFStat(void)
2094{
2095 RTTestISub("fstat");
2096 RTFILE hFile1;
2097 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file2")),
2098 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2099 RTFSOBJINFO ObjInfo = {0};
2100 PROFILE_FN(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_NOTHING), g_nsTestRun, "RTFileQueryInfo/NOTHING");
2101 PROFILE_FN(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_UNIX), g_nsTestRun, "RTFileQueryInfo/UNIX");
2102
2103 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2104}
2105
2106#ifdef RT_OS_WINDOWS
2107/**
2108 * Nt(Query|Set|QueryDir)Information(File|) information class info.
2109 */
2110static const struct
2111{
2112 const char *pszName;
2113 int enmValue;
2114 bool fQuery;
2115 bool fSet;
2116 bool fQueryDir;
2117 uint8_t cbMin;
2118} g_aNtQueryInfoFileClasses[] =
2119{
2120#define E(a_enmValue, a_fQuery, a_fSet, a_fQueryDir, a_cbMin) \
2121 { #a_enmValue, a_enmValue, a_fQuery, a_fSet, a_fQueryDir, a_cbMin }
2122 { "invalid0", 0, false, false, false, 0 },
2123 E(FileDirectoryInformation, false, false, true, sizeof(FILE_DIRECTORY_INFORMATION)), // 0x00, 0x00, 0x48
2124 E(FileFullDirectoryInformation, false, false, true, sizeof(FILE_FULL_DIR_INFORMATION)), // 0x00, 0x00, 0x48
2125 E(FileBothDirectoryInformation, false, false, true, sizeof(FILE_BOTH_DIR_INFORMATION)), // 0x00, 0x00, 0x60
2126 E(FileBasicInformation, true, true, false, sizeof(FILE_BASIC_INFORMATION)),
2127 E(FileStandardInformation, true, false, false, sizeof(FILE_STANDARD_INFORMATION)),
2128 E(FileInternalInformation, true, false, false, sizeof(FILE_INTERNAL_INFORMATION)),
2129 E(FileEaInformation, true, false, false, sizeof(FILE_EA_INFORMATION)),
2130 E(FileAccessInformation, true, false, false, sizeof(FILE_ACCESS_INFORMATION)),
2131 E(FileNameInformation, true, false, false, sizeof(FILE_NAME_INFORMATION)),
2132 E(FileRenameInformation, false, true, false, sizeof(FILE_RENAME_INFORMATION)),
2133 E(FileLinkInformation, false, true, false, sizeof(FILE_LINK_INFORMATION)),
2134 E(FileNamesInformation, false, false, true, sizeof(FILE_NAMES_INFORMATION)), // 0x00, 0x00, 0x10
2135 E(FileDispositionInformation, false, true, false, sizeof(FILE_DISPOSITION_INFORMATION)), // 0x00, 0x01,
2136 E(FilePositionInformation, true, true, false, sizeof(FILE_POSITION_INFORMATION)), // 0x08, 0x08,
2137 E(FileFullEaInformation, false, false, false, sizeof(FILE_FULL_EA_INFORMATION)), // 0x00, 0x00,
2138 E(FileModeInformation, true, true, false, sizeof(FILE_MODE_INFORMATION)), // 0x04, 0x04,
2139 E(FileAlignmentInformation, true, false, false, sizeof(FILE_ALIGNMENT_INFORMATION)), // 0x04, 0x00,
2140 E(FileAllInformation, true, false, false, sizeof(FILE_ALL_INFORMATION)), // 0x68, 0x00,
2141 E(FileAllocationInformation, false, true, false, sizeof(FILE_ALLOCATION_INFORMATION)), // 0x00, 0x08,
2142 E(FileEndOfFileInformation, false, true, false, sizeof(FILE_END_OF_FILE_INFORMATION)), // 0x00, 0x08,
2143 E(FileAlternateNameInformation, true, false, false, sizeof(FILE_NAME_INFORMATION)), // 0x08, 0x00,
2144 E(FileStreamInformation, true, false, false, sizeof(FILE_STREAM_INFORMATION)), // 0x20, 0x00,
2145 E(FilePipeInformation, true, true, false, sizeof(FILE_PIPE_INFORMATION)), // 0x08, 0x08,
2146 E(FilePipeLocalInformation, true, false, false, sizeof(FILE_PIPE_LOCAL_INFORMATION)), // 0x28, 0x00,
2147 E(FilePipeRemoteInformation, true, true, false, sizeof(FILE_PIPE_REMOTE_INFORMATION)), // 0x10, 0x10,
2148 E(FileMailslotQueryInformation, true, false, false, sizeof(FILE_MAILSLOT_QUERY_INFORMATION)), // 0x18, 0x00,
2149 E(FileMailslotSetInformation, false, true, false, sizeof(FILE_MAILSLOT_SET_INFORMATION)), // 0x00, 0x08,
2150 E(FileCompressionInformation, true, false, false, sizeof(FILE_COMPRESSION_INFORMATION)), // 0x10, 0x00,
2151 E(FileObjectIdInformation, true, true, true, sizeof(FILE_OBJECTID_INFORMATION)), // 0x48, 0x48,
2152 E(FileCompletionInformation, false, true, false, sizeof(FILE_COMPLETION_INFORMATION)), // 0x00, 0x10,
2153 E(FileMoveClusterInformation, false, true, false, sizeof(FILE_MOVE_CLUSTER_INFORMATION)), // 0x00, 0x18,
2154 E(FileQuotaInformation, true, true, true, sizeof(FILE_QUOTA_INFORMATION)), // 0x38, 0x38, 0x38
2155 E(FileReparsePointInformation, true, false, true, sizeof(FILE_REPARSE_POINT_INFORMATION)), // 0x10, 0x00, 0x10
2156 E(FileNetworkOpenInformation, true, false, false, sizeof(FILE_NETWORK_OPEN_INFORMATION)), // 0x38, 0x00,
2157 E(FileAttributeTagInformation, true, false, false, sizeof(FILE_ATTRIBUTE_TAG_INFORMATION)), // 0x08, 0x00,
2158 E(FileTrackingInformation, false, true, false, sizeof(FILE_TRACKING_INFORMATION)), // 0x00, 0x10,
2159 E(FileIdBothDirectoryInformation, false, false, true, sizeof(FILE_ID_BOTH_DIR_INFORMATION)), // 0x00, 0x00, 0x70
2160 E(FileIdFullDirectoryInformation, false, false, true, sizeof(FILE_ID_FULL_DIR_INFORMATION)), // 0x00, 0x00, 0x58
2161 E(FileValidDataLengthInformation, false, true, false, sizeof(FILE_VALID_DATA_LENGTH_INFORMATION)), // 0x00, 0x08,
2162 E(FileShortNameInformation, false, true, false, sizeof(FILE_NAME_INFORMATION)), // 0x00, 0x08,
2163 E(FileIoCompletionNotificationInformation, true, true, false, sizeof(FILE_IO_COMPLETION_NOTIFICATION_INFORMATION)), // 0x04, 0x04,
2164 E(FileIoStatusBlockRangeInformation, false, true, false, sizeof(IO_STATUS_BLOCK) /*?*/), // 0x00, 0x10,
2165 E(FileIoPriorityHintInformation, true, true, false, sizeof(FILE_IO_PRIORITY_HINT_INFORMATION)), // 0x04, 0x04,
2166 E(FileSfioReserveInformation, true, true, false, sizeof(FILE_SFIO_RESERVE_INFORMATION)), // 0x14, 0x14,
2167 E(FileSfioVolumeInformation, true, false, false, sizeof(FILE_SFIO_VOLUME_INFORMATION)), // 0x0C, 0x00,
2168 E(FileHardLinkInformation, true, false, false, sizeof(FILE_LINKS_INFORMATION)), // 0x20, 0x00,
2169 E(FileProcessIdsUsingFileInformation, true, false, false, sizeof(FILE_PROCESS_IDS_USING_FILE_INFORMATION)), // 0x10, 0x00,
2170 E(FileNormalizedNameInformation, true, false, false, sizeof(FILE_NAME_INFORMATION)), // 0x08, 0x00,
2171 E(FileNetworkPhysicalNameInformation, true, false, false, sizeof(FILE_NETWORK_PHYSICAL_NAME_INFORMATION)), // 0x08, 0x00,
2172 E(FileIdGlobalTxDirectoryInformation, false, false, true, sizeof(FILE_ID_GLOBAL_TX_DIR_INFORMATION)), // 0x00, 0x00, 0x60
2173 E(FileIsRemoteDeviceInformation, true, false, false, sizeof(FILE_IS_REMOTE_DEVICE_INFORMATION)), // 0x01, 0x00,
2174 E(FileUnusedInformation, false, false, false, 0), // 0x00, 0x00,
2175 E(FileNumaNodeInformation, true, false, false, sizeof(FILE_NUMA_NODE_INFORMATION)), // 0x02, 0x00,
2176 E(FileStandardLinkInformation, true, false, false, sizeof(FILE_STANDARD_LINK_INFORMATION)), // 0x0C, 0x00,
2177 E(FileRemoteProtocolInformation, true, false, false, sizeof(FILE_REMOTE_PROTOCOL_INFORMATION)), // 0x74, 0x00,
2178 E(FileRenameInformationBypassAccessCheck, false, false, false, 0 /*kernel mode only*/), // 0x00, 0x00,
2179 E(FileLinkInformationBypassAccessCheck, false, false, false, 0 /*kernel mode only*/), // 0x00, 0x00,
2180 E(FileVolumeNameInformation, true, false, false, sizeof(FILE_VOLUME_NAME_INFORMATION)), // 0x08, 0x00,
2181 E(FileIdInformation, true, false, false, sizeof(FILE_ID_INFORMATION)), // 0x18, 0x00,
2182 E(FileIdExtdDirectoryInformation, false, false, true, sizeof(FILE_ID_EXTD_DIR_INFORMATION)), // 0x00, 0x00, 0x60
2183 E(FileReplaceCompletionInformation, false, true, false, sizeof(FILE_COMPLETION_INFORMATION)), // 0x00, 0x10,
2184 E(FileHardLinkFullIdInformation, true, false, false, sizeof(FILE_LINK_ENTRY_FULL_ID_INFORMATION)), // 0x24, 0x00,
2185 E(FileIdExtdBothDirectoryInformation, false, false, true, sizeof(FILE_ID_EXTD_BOTH_DIR_INFORMATION)), // 0x00, 0x00, 0x78
2186 E(FileDispositionInformationEx, false, true, false, sizeof(FILE_DISPOSITION_INFORMATION_EX)), // 0x00, 0x04,
2187 E(FileRenameInformationEx, false, true, false, sizeof(FILE_RENAME_INFORMATION)), // 0x00, 0x18,
2188 E(FileRenameInformationExBypassAccessCheck, false, false, false, 0 /*kernel mode only*/), // 0x00, 0x00,
2189 E(FileDesiredStorageClassInformation, true, true, false, sizeof(FILE_DESIRED_STORAGE_CLASS_INFORMATION)), // 0x08, 0x08,
2190 E(FileStatInformation, true, false, false, sizeof(FILE_STAT_INFORMATION)), // 0x48, 0x00,
2191 E(FileMemoryPartitionInformation, false, true, false, 0x10), // 0x00, 0x10,
2192 E(FileStatLxInformation, true, false, false, sizeof(FILE_STAT_LX_INFORMATION)), // 0x60, 0x00,
2193 E(FileCaseSensitiveInformation, true, true, false, sizeof(FILE_CASE_SENSITIVE_INFORMATION)), // 0x04, 0x04,
2194 E(FileLinkInformationEx, false, true, false, sizeof(FILE_LINK_INFORMATION)), // 0x00, 0x18,
2195 E(FileLinkInformationExBypassAccessCheck, false, false, false, 0 /*kernel mode only*/), // 0x00, 0x00,
2196 E(FileStorageReserveIdInformation, true, true, false, 0x04), // 0x04, 0x04,
2197 E(FileCaseSensitiveInformationForceAccessCheck, true, true, false, sizeof(FILE_CASE_SENSITIVE_INFORMATION)), // 0x04, 0x04,
2198#undef E
2199};
2200
2201void fsPerfNtQueryInfoFileWorker(HANDLE hNtFile1, uint32_t fType)
2202{
2203 /** @todo may run out of buffer for really long paths? */
2204 union
2205 {
2206 uint8_t ab[4096];
2207 FILE_ACCESS_INFORMATION Access;
2208 FILE_ALIGNMENT_INFORMATION Align;
2209 FILE_ALL_INFORMATION All;
2210 FILE_ALLOCATION_INFORMATION Alloc;
2211 FILE_ATTRIBUTE_TAG_INFORMATION AttribTag;
2212 FILE_BASIC_INFORMATION Basic;
2213 FILE_BOTH_DIR_INFORMATION BothDir;
2214 FILE_CASE_SENSITIVE_INFORMATION CaseSensitivity;
2215 FILE_COMPLETION_INFORMATION Completion;
2216 FILE_COMPRESSION_INFORMATION Compression;
2217 FILE_DESIRED_STORAGE_CLASS_INFORMATION StorageClass;
2218 FILE_DIRECTORY_INFORMATION Dir;
2219 FILE_DISPOSITION_INFORMATION Disp;
2220 FILE_DISPOSITION_INFORMATION_EX DispEx;
2221 FILE_EA_INFORMATION Ea;
2222 FILE_END_OF_FILE_INFORMATION EndOfFile;
2223 FILE_FULL_DIR_INFORMATION FullDir;
2224 FILE_FULL_EA_INFORMATION FullEa;
2225 FILE_ID_BOTH_DIR_INFORMATION IdBothDir;
2226 FILE_ID_EXTD_BOTH_DIR_INFORMATION ExtIdBothDir;
2227 FILE_ID_EXTD_DIR_INFORMATION ExtIdDir;
2228 FILE_ID_FULL_DIR_INFORMATION IdFullDir;
2229 FILE_ID_GLOBAL_TX_DIR_INFORMATION IdGlobalTx;
2230 FILE_ID_INFORMATION IdInfo;
2231 FILE_INTERNAL_INFORMATION Internal;
2232 FILE_IO_COMPLETION_NOTIFICATION_INFORMATION IoCompletion;
2233 FILE_IO_PRIORITY_HINT_INFORMATION IoPrioHint;
2234 FILE_IS_REMOTE_DEVICE_INFORMATION IsRemoteDev;
2235 FILE_LINK_ENTRY_FULL_ID_INFORMATION LinkFullId;
2236 FILE_LINK_INFORMATION Link;
2237 FILE_MAILSLOT_QUERY_INFORMATION MailslotQuery;
2238 FILE_MAILSLOT_SET_INFORMATION MailslotSet;
2239 FILE_MODE_INFORMATION Mode;
2240 FILE_MOVE_CLUSTER_INFORMATION MoveCluster;
2241 FILE_NAME_INFORMATION Name;
2242 FILE_NAMES_INFORMATION Names;
2243 FILE_NETWORK_OPEN_INFORMATION NetOpen;
2244 FILE_NUMA_NODE_INFORMATION Numa;
2245 FILE_OBJECTID_INFORMATION ObjId;
2246 FILE_PIPE_INFORMATION Pipe;
2247 FILE_PIPE_LOCAL_INFORMATION PipeLocal;
2248 FILE_PIPE_REMOTE_INFORMATION PipeRemote;
2249 FILE_POSITION_INFORMATION Pos;
2250 FILE_PROCESS_IDS_USING_FILE_INFORMATION Pids;
2251 FILE_QUOTA_INFORMATION Quota;
2252 FILE_REMOTE_PROTOCOL_INFORMATION RemoteProt;
2253 FILE_RENAME_INFORMATION Rename;
2254 FILE_REPARSE_POINT_INFORMATION Reparse;
2255 FILE_SFIO_RESERVE_INFORMATION SfiRes;
2256 FILE_SFIO_VOLUME_INFORMATION SfioVol;
2257 FILE_STANDARD_INFORMATION Std;
2258 FILE_STANDARD_LINK_INFORMATION StdLink;
2259 FILE_STAT_INFORMATION Stat;
2260 FILE_STAT_LX_INFORMATION StatLx;
2261 FILE_STREAM_INFORMATION Stream;
2262 FILE_TRACKING_INFORMATION Tracking;
2263 FILE_VALID_DATA_LENGTH_INFORMATION ValidDataLen;
2264 FILE_VOLUME_NAME_INFORMATION VolName;
2265 } uBuf;
2266
2267 IO_STATUS_BLOCK const VirginIos = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2268 for (unsigned i = 0; i < RT_ELEMENTS(g_aNtQueryInfoFileClasses); i++)
2269 {
2270 FILE_INFORMATION_CLASS const enmClass = (FILE_INFORMATION_CLASS)g_aNtQueryInfoFileClasses[i].enmValue;
2271 const char * const pszClass = g_aNtQueryInfoFileClasses[i].pszName;
2272
2273 memset(&uBuf, 0xff, sizeof(uBuf));
2274 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2275 ULONG cbBuf = sizeof(uBuf);
2276 NTSTATUS rcNt = NtQueryInformationFile(hNtFile1, &Ios, &uBuf, cbBuf, enmClass);
2277 if (NT_SUCCESS(rcNt))
2278 {
2279 if (Ios.Status == VirginIos.Status || Ios.Information == VirginIos.Information)
2280 RTTestIFailed("%s/%#x: I/O status block was not modified: %#x %#zx", pszClass, cbBuf, Ios.Status, Ios.Information);
2281 else if (!g_aNtQueryInfoFileClasses[i].fQuery)
2282 RTTestIFailed("%s/%#x: This isn't supposed to be queriable! (rcNt=%#x)", pszClass, cbBuf, rcNt);
2283 else
2284 {
2285 ULONG const cbActualMin = enmClass != FileStorageReserveIdInformation ? Ios.Information : 4; /* weird */
2286
2287 switch (enmClass)
2288 {
2289 case FileNameInformation:
2290 case FileAlternateNameInformation:
2291 case FileShortNameInformation:
2292 case FileNormalizedNameInformation:
2293 case FileNetworkPhysicalNameInformation:
2294 if ( RT_UOFFSETOF_DYN(FILE_NAME_INFORMATION, FileName[uBuf.Name.FileNameLength / sizeof(WCHAR)])
2295 != cbActualMin)
2296 RTTestIFailed("%s/%#x: Wrong FileNameLength=%#x vs cbActual=%#x",
2297 pszClass, cbActualMin, uBuf.Name.FileNameLength, cbActualMin);
2298 if (uBuf.Name.FileName[uBuf.Name.FileNameLength / sizeof(WCHAR) - 1] == '\0')
2299 RTTestIFailed("%s/%#x: Zero terminated name!", pszClass, cbActualMin);
2300 if (g_uVerbosity > 1)
2301 RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#x: FileNameLength=%#x FileName='%.*ls'\n",
2302 pszClass, cbActualMin, uBuf.Name.FileNameLength,
2303 uBuf.Name.FileNameLength / sizeof(WCHAR), uBuf.Name.FileName);
2304 break;
2305
2306 case FileVolumeNameInformation:
2307 if (RT_UOFFSETOF_DYN(FILE_VOLUME_NAME_INFORMATION,
2308 DeviceName[uBuf.VolName.DeviceNameLength / sizeof(WCHAR)]) != cbActualMin)
2309 RTTestIFailed("%s/%#x: Wrong DeviceNameLength=%#x vs cbActual=%#x",
2310 pszClass, cbActualMin, uBuf.VolName.DeviceNameLength, cbActualMin);
2311 if (uBuf.VolName.DeviceName[uBuf.VolName.DeviceNameLength / sizeof(WCHAR) - 1] == '\0')
2312 RTTestIFailed("%s/%#x: Zero terminated name!", pszClass, cbActualMin);
2313 if (g_uVerbosity > 1)
2314 RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#x: DeviceNameLength=%#x DeviceName='%.*ls'\n",
2315 pszClass, cbActualMin, uBuf.VolName.DeviceNameLength,
2316 uBuf.VolName.DeviceNameLength / sizeof(WCHAR), uBuf.VolName.DeviceName);
2317 break;
2318 default:
2319 break;
2320 }
2321
2322 ULONG const cbMin = g_aNtQueryInfoFileClasses[i].cbMin;
2323 ULONG const cbMax = RT_MIN(cbActualMin + 64, sizeof(uBuf));
2324 for (cbBuf = 0; cbBuf < cbMax; cbBuf++)
2325 {
2326 memset(&uBuf, 0xfe, sizeof(uBuf));
2327 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
2328 rcNt = NtQueryInformationFile(hNtFile1, &Ios, &uBuf, cbBuf, enmClass);
2329 if (!ASMMemIsAllU8(&uBuf.ab[cbBuf], sizeof(uBuf) - cbBuf, 0xfe))
2330 RTTestIFailed("%s/%#x: Touched memory beyond end of buffer (rcNt=%#x)", pszClass, cbBuf, rcNt);
2331 if (cbBuf < cbMin)
2332 {
2333 if (rcNt != STATUS_INFO_LENGTH_MISMATCH)
2334 RTTestIFailed("%s/%#x: %#x, expected STATUS_INFO_LENGTH_MISMATCH", pszClass, cbBuf, rcNt);
2335 if (Ios.Status != VirginIos.Status || Ios.Information != VirginIos.Information)
2336 RTTestIFailed("%s/%#x: I/O status block was modified (STATUS_INFO_LENGTH_MISMATCH): %#x %#zx",
2337 pszClass, cbBuf, Ios.Status, Ios.Information);
2338 }
2339 else if (cbBuf < cbActualMin)
2340 {
2341 if ( rcNt != STATUS_BUFFER_OVERFLOW
2342 /* RDR2/w10 returns success if the buffer can hold exactly the share name: */
2343 && !( rcNt == STATUS_SUCCESS
2344 && enmClass == FileNetworkPhysicalNameInformation)
2345 )
2346 RTTestIFailed("%s/%#x: %#x, expected STATUS_BUFFER_OVERFLOW", pszClass, cbBuf, rcNt);
2347 /** @todo check name and length fields */
2348 }
2349 else
2350 {
2351 if ( !ASMMemIsAllU8(&uBuf.ab[cbActualMin], sizeof(uBuf) - cbActualMin, 0xfe)
2352 && enmClass != FileStorageReserveIdInformation /* NTFS bug? */ )
2353 RTTestIFailed("%s/%#x: Touched memory beyond returned length (cbActualMin=%#x, rcNt=%#x)",
2354 pszClass, cbBuf, cbActualMin, rcNt);
2355
2356 }
2357 }
2358 }
2359 }
2360 else
2361 {
2362 if (!g_aNtQueryInfoFileClasses[i].fQuery)
2363 {
2364 if (rcNt != STATUS_INVALID_INFO_CLASS)
2365 RTTestIFailed("%s/%#x: %#x, expected STATUS_INVALID_INFO_CLASS", pszClass, cbBuf, rcNt);
2366 }
2367 else if ( rcNt != STATUS_INVALID_INFO_CLASS
2368 && rcNt != STATUS_INVALID_PARAMETER
2369 && !(rcNt == STATUS_OBJECT_NAME_NOT_FOUND && enmClass == FileAlternateNameInformation)
2370 && !( rcNt == STATUS_ACCESS_DENIED
2371 && ( enmClass == FileIoPriorityHintInformation
2372 || enmClass == FileSfioReserveInformation
2373 || enmClass == FileStatLxInformation))
2374 && !(rcNt == STATUS_NO_SUCH_DEVICE && enmClass == FileNumaNodeInformation)
2375 && !( rcNt == STATUS_NOT_SUPPORTED /* RDR2/W10-17763 */
2376 && ( enmClass == FileMailslotQueryInformation
2377 || enmClass == FileObjectIdInformation
2378 || enmClass == FileReparsePointInformation
2379 || enmClass == FileSfioVolumeInformation
2380 || enmClass == FileHardLinkInformation
2381 || enmClass == FileStandardLinkInformation
2382 || enmClass == FileHardLinkFullIdInformation
2383 || enmClass == FileDesiredStorageClassInformation
2384 || enmClass == FileStatInformation
2385 || enmClass == FileCaseSensitiveInformation
2386 || enmClass == FileStorageReserveIdInformation
2387 || enmClass == FileCaseSensitiveInformationForceAccessCheck)
2388 || ( fType == RTFS_TYPE_DIRECTORY
2389 && (enmClass == FileSfioReserveInformation || enmClass == FileStatLxInformation)))
2390 )
2391 RTTestIFailed("%s/%#x: %#x", pszClass, cbBuf, rcNt);
2392 if ( (Ios.Status != VirginIos.Status || Ios.Information != VirginIos.Information)
2393 && !( fType == RTFS_TYPE_DIRECTORY /* NTFS/W10-17763 */
2394 && Ios.Status == rcNt && Ios.Information == 0) )
2395 RTTestIFailed("%s/%#x: I/O status block was modified: %#x %#zx", pszClass, cbBuf, Ios.Status, Ios.Information);
2396 if (!ASMMemIsAllU8(&uBuf, sizeof(uBuf), 0xff))
2397 RTTestIFailed("%s/%#x: Buffer was touched in failure case!", pszClass, cbBuf);
2398 }
2399 }
2400}
2401
2402void fsPerfNtQueryInfoFile(void)
2403{
2404 RTTestISub("NtQueryInformationFile");
2405
2406 /* On a regular file: */
2407 RTFILE hFile1;
2408 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file2qif")),
2409 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
2410 fsPerfNtQueryInfoFileWorker((HANDLE)RTFileToNative(hFile1), RTFS_TYPE_FILE);
2411 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2412
2413 /* On a directory: */
2414 HANDLE hDir1 = INVALID_HANDLE_VALUE;
2415 RTTESTI_CHECK_RC_RETV(RTNtPathOpenDir(InDir(RT_STR_TUPLE("")), GENERIC_READ | SYNCHRONIZE | FILE_SYNCHRONOUS_IO_NONALERT,
2416 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
2417 FILE_OPEN, 0, &hDir1, NULL), VINF_SUCCESS);
2418 fsPerfNtQueryInfoFileWorker(hDir1, RTFS_TYPE_DIRECTORY);
2419 RTTESTI_CHECK(CloseHandle(hDir1) == TRUE);
2420}
2421
2422
2423/**
2424 * Nt(Query|Set)VolumeInformationFile) information class info.
2425 */
2426static const struct
2427{
2428 const char *pszName;
2429 int enmValue;
2430 bool fQuery;
2431 bool fSet;
2432 uint8_t cbMin;
2433} g_aNtQueryVolInfoFileClasses[] =
2434{
2435#define E(a_enmValue, a_fQuery, a_fSet, a_cbMin) \
2436 { #a_enmValue, a_enmValue, a_fQuery, a_fSet, a_cbMin }
2437 { "invalid0", 0, false, false, 0 },
2438 E(FileFsVolumeInformation, 1, 0, sizeof(FILE_FS_VOLUME_INFORMATION)),
2439 E(FileFsLabelInformation, 0, 1, sizeof(FILE_FS_LABEL_INFORMATION)),
2440 E(FileFsSizeInformation, 1, 0, sizeof(FILE_FS_SIZE_INFORMATION)),
2441 E(FileFsDeviceInformation, 1, 0, sizeof(FILE_FS_DEVICE_INFORMATION)),
2442 E(FileFsAttributeInformation, 1, 0, sizeof(FILE_FS_ATTRIBUTE_INFORMATION)),
2443 E(FileFsControlInformation, 1, 1, sizeof(FILE_FS_CONTROL_INFORMATION)),
2444 E(FileFsFullSizeInformation, 1, 0, sizeof(FILE_FS_FULL_SIZE_INFORMATION)),
2445 E(FileFsObjectIdInformation, 1, 1, sizeof(FILE_FS_OBJECTID_INFORMATION)),
2446 E(FileFsDriverPathInformation, 1, 0, sizeof(FILE_FS_DRIVER_PATH_INFORMATION)),
2447 E(FileFsVolumeFlagsInformation, 1, 1, sizeof(FILE_FS_VOLUME_FLAGS_INFORMATION)),
2448 E(FileFsSectorSizeInformation, 1, 0, sizeof(FILE_FS_SECTOR_SIZE_INFORMATION)),
2449 E(FileFsDataCopyInformation, 1, 0, sizeof(FILE_FS_DATA_COPY_INFORMATION)),
2450 E(FileFsMetadataSizeInformation, 1, 0, sizeof(FILE_FS_METADATA_SIZE_INFORMATION)),
2451 E(FileFsFullSizeInformationEx, 1, 0, sizeof(FILE_FS_FULL_SIZE_INFORMATION_EX)),
2452#undef E
2453};
2454
2455void fsPerfNtQueryVolInfoFileWorker(HANDLE hNtFile1, uint32_t fType)
2456{
2457 char const chType = fType == RTFS_TYPE_DIRECTORY ? 'd' : 'r';
2458 union
2459 {
2460 uint8_t ab[4096];
2461 FILE_FS_VOLUME_INFORMATION Vol;
2462 FILE_FS_LABEL_INFORMATION Label;
2463 FILE_FS_SIZE_INFORMATION Size;
2464 FILE_FS_DEVICE_INFORMATION Dev;
2465 FILE_FS_ATTRIBUTE_INFORMATION Attrib;
2466 FILE_FS_CONTROL_INFORMATION Ctrl;
2467 FILE_FS_FULL_SIZE_INFORMATION FullSize;
2468 FILE_FS_OBJECTID_INFORMATION ObjId;
2469 FILE_FS_DRIVER_PATH_INFORMATION DrvPath;
2470 FILE_FS_VOLUME_FLAGS_INFORMATION VolFlags;
2471 FILE_FS_SECTOR_SIZE_INFORMATION SectorSize;
2472 FILE_FS_DATA_COPY_INFORMATION DataCopy;
2473 FILE_FS_METADATA_SIZE_INFORMATION Metadata;
2474 FILE_FS_FULL_SIZE_INFORMATION_EX FullSizeEx;
2475 } uBuf;
2476
2477 IO_STATUS_BLOCK const VirginIos = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2478 for (unsigned i = 0; i < RT_ELEMENTS(g_aNtQueryVolInfoFileClasses); i++)
2479 {
2480 FS_INFORMATION_CLASS const enmClass = (FS_INFORMATION_CLASS)g_aNtQueryVolInfoFileClasses[i].enmValue;
2481 const char * const pszClass = g_aNtQueryVolInfoFileClasses[i].pszName;
2482
2483 memset(&uBuf, 0xff, sizeof(uBuf));
2484 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
2485 ULONG cbBuf = sizeof(uBuf);
2486 NTSTATUS rcNt = NtQueryVolumeInformationFile(hNtFile1, &Ios, &uBuf, cbBuf, enmClass);
2487 if (g_uVerbosity > 3)
2488 RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c: rcNt=%#x Ios.Status=%#x Info=%#zx\n",
2489 pszClass, cbBuf, chType, rcNt, Ios.Status, Ios.Information);
2490 if (NT_SUCCESS(rcNt))
2491 {
2492 if (Ios.Status == VirginIos.Status || Ios.Information == VirginIos.Information)
2493 RTTestIFailed("%s/%#x/%c: I/O status block was not modified: %#x %#zx",
2494 pszClass, cbBuf, chType, Ios.Status, Ios.Information);
2495 else if (!g_aNtQueryVolInfoFileClasses[i].fQuery)
2496 RTTestIFailed("%s/%#x/%c: This isn't supposed to be queriable! (rcNt=%#x)", pszClass, cbBuf, chType, rcNt);
2497 else
2498 {
2499 ULONG const cbActualMin = Ios.Information;
2500 ULONG *pcbName = NULL;
2501 ULONG offName = 0;
2502
2503 switch (enmClass)
2504 {
2505 case FileFsVolumeInformation:
2506 pcbName = &uBuf.Vol.VolumeLabelLength;
2507 offName = RT_UOFFSETOF(FILE_FS_VOLUME_INFORMATION, VolumeLabel);
2508 if (RT_UOFFSETOF_DYN(FILE_FS_VOLUME_INFORMATION,
2509 VolumeLabel[uBuf.Vol.VolumeLabelLength / sizeof(WCHAR)]) != cbActualMin)
2510 RTTestIFailed("%s/%#x/%c: Wrong VolumeLabelLength=%#x vs cbActual=%#x",
2511 pszClass, cbActualMin, chType, uBuf.Vol.VolumeLabelLength, cbActualMin);
2512 if (uBuf.Vol.VolumeLabel[uBuf.Vol.VolumeLabelLength / sizeof(WCHAR) - 1] == '\0')
2513 RTTestIFailed("%s/%#x/%c: Zero terminated name!", pszClass, cbActualMin, chType);
2514 if (g_uVerbosity > 1)
2515 RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c: VolumeLabelLength=%#x VolumeLabel='%.*ls'\n",
2516 pszClass, cbActualMin, chType, uBuf.Vol.VolumeLabelLength,
2517 uBuf.Vol.VolumeLabelLength / sizeof(WCHAR), uBuf.Vol.VolumeLabel);
2518 break;
2519
2520 case FileFsAttributeInformation:
2521 pcbName = &uBuf.Attrib.FileSystemNameLength;
2522 offName = RT_UOFFSETOF(FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName);
2523 if (RT_UOFFSETOF_DYN(FILE_FS_ATTRIBUTE_INFORMATION,
2524 FileSystemName[uBuf.Attrib.FileSystemNameLength / sizeof(WCHAR)]) != cbActualMin)
2525 RTTestIFailed("%s/%#x/%c: Wrong FileSystemNameLength=%#x vs cbActual=%#x",
2526 pszClass, cbActualMin, chType, uBuf.Attrib.FileSystemNameLength, cbActualMin);
2527 if (uBuf.Attrib.FileSystemName[uBuf.Attrib.FileSystemNameLength / sizeof(WCHAR) - 1] == '\0')
2528 RTTestIFailed("%s/%#x/%c: Zero terminated name!", pszClass, cbActualMin, chType);
2529 if (g_uVerbosity > 1)
2530 RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c: FileSystemNameLength=%#x FileSystemName='%.*ls' Attribs=%#x MaxCompName=%#x\n",
2531 pszClass, cbActualMin, chType, uBuf.Attrib.FileSystemNameLength,
2532 uBuf.Attrib.FileSystemNameLength / sizeof(WCHAR), uBuf.Attrib.FileSystemName,
2533 uBuf.Attrib.FileSystemAttributes, uBuf.Attrib.MaximumComponentNameLength);
2534 break;
2535
2536 case FileFsDriverPathInformation:
2537 pcbName = &uBuf.DrvPath.DriverNameLength;
2538 offName = RT_UOFFSETOF(FILE_FS_DRIVER_PATH_INFORMATION, DriverName);
2539 if (RT_UOFFSETOF_DYN(FILE_FS_DRIVER_PATH_INFORMATION,
2540 DriverName[uBuf.DrvPath.DriverNameLength / sizeof(WCHAR)]) != cbActualMin)
2541 RTTestIFailed("%s/%#x/%c: Wrong DriverNameLength=%#x vs cbActual=%#x",
2542 pszClass, cbActualMin, chType, uBuf.DrvPath.DriverNameLength, cbActualMin);
2543 if (uBuf.DrvPath.DriverName[uBuf.DrvPath.DriverNameLength / sizeof(WCHAR) - 1] == '\0')
2544 RTTestIFailed("%s/%#x/%c: Zero terminated name!", pszClass, cbActualMin, chType);
2545 if (g_uVerbosity > 1)
2546 RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c: DriverNameLength=%#x DriverName='%.*ls'\n",
2547 pszClass, cbActualMin, chType, uBuf.DrvPath.DriverNameLength,
2548 uBuf.DrvPath.DriverNameLength / sizeof(WCHAR), uBuf.DrvPath.DriverName);
2549 break;
2550
2551 case FileFsSectorSizeInformation:
2552 if (g_uVerbosity > 1)
2553 RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c: Flags=%#x log=%#x atomic=%#x perf=%#x eff=%#x offSec=%#x offPart=%#x\n",
2554 pszClass, cbActualMin, chType, uBuf.SectorSize.Flags,
2555 uBuf.SectorSize.LogicalBytesPerSector,
2556 uBuf.SectorSize.PhysicalBytesPerSectorForAtomicity,
2557 uBuf.SectorSize.PhysicalBytesPerSectorForPerformance,
2558 uBuf.SectorSize.FileSystemEffectivePhysicalBytesPerSectorForAtomicity,
2559 uBuf.SectorSize.ByteOffsetForSectorAlignment,
2560 uBuf.SectorSize.ByteOffsetForPartitionAlignment);
2561 break;
2562
2563 default:
2564 if (g_uVerbosity > 2)
2565 RTTestIPrintf(RTTESTLVL_ALWAYS, "%+34s/%#04x/%c:\n", pszClass, cbActualMin, chType);
2566 break;
2567 }
2568 ULONG const cbName = pcbName ? *pcbName : 0;
2569 uint8_t abNameCopy[4096];
2570 RT_ZERO(abNameCopy);
2571 if (pcbName)
2572 memcpy(abNameCopy, &uBuf.ab[offName], cbName);
2573
2574 ULONG const cbMin = g_aNtQueryVolInfoFileClasses[i].cbMin;
2575 ULONG const cbMax = RT_MIN(cbActualMin + 64, sizeof(uBuf));
2576 for (cbBuf = 0; cbBuf < cbMax; cbBuf++)
2577 {
2578 memset(&uBuf, 0xfe, sizeof(uBuf));
2579 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
2580 rcNt = NtQueryVolumeInformationFile(hNtFile1, &Ios, &uBuf, cbBuf, enmClass);
2581 if (!ASMMemIsAllU8(&uBuf.ab[cbBuf], sizeof(uBuf) - cbBuf, 0xfe))
2582 RTTestIFailed("%s/%#x/%c: Touched memory beyond end of buffer (rcNt=%#x)", pszClass, cbBuf, chType, rcNt);
2583 if (cbBuf < cbMin)
2584 {
2585 if (rcNt != STATUS_INFO_LENGTH_MISMATCH)
2586 RTTestIFailed("%s/%#x/%c: %#x, expected STATUS_INFO_LENGTH_MISMATCH", pszClass, cbBuf, chType, rcNt);
2587 if (Ios.Status != VirginIos.Status || Ios.Information != VirginIos.Information)
2588 RTTestIFailed("%s/%#x/%c: I/O status block was modified (STATUS_INFO_LENGTH_MISMATCH): %#x %#zx",
2589 pszClass, cbBuf, chType, Ios.Status, Ios.Information);
2590 }
2591 else if (cbBuf < cbActualMin)
2592 {
2593 if (rcNt != STATUS_BUFFER_OVERFLOW)
2594 RTTestIFailed("%s/%#x/%c: %#x, expected STATUS_BUFFER_OVERFLOW", pszClass, cbBuf, chType, rcNt);
2595 if (pcbName)
2596 {
2597 size_t const cbNameAlt = offName < cbBuf ? cbBuf - offName : 0;
2598 if ( *pcbName != cbName
2599 && !( *pcbName == cbNameAlt
2600 && (enmClass == FileFsAttributeInformation /*NTFS,FAT*/)))
2601 RTTestIFailed("%s/%#x/%c: Wrong name length: %#x, expected %#x (or %#x)",
2602 pszClass, cbBuf, chType, *pcbName, cbName, cbNameAlt);
2603 if (memcmp(abNameCopy, &uBuf.ab[offName], cbNameAlt) != 0)
2604 RTTestIFailed("%s/%#x/%c: Wrong partial name: %.*Rhxs",
2605 pszClass, cbBuf, chType, cbNameAlt, &uBuf.ab[offName]);
2606 }
2607 if (Ios.Information != cbBuf)
2608 RTTestIFailed("%s/%#x/%c: Ios.Information = %#x, expected %#x",
2609 pszClass, cbBuf, chType, Ios.Information, cbBuf);
2610 }
2611 else
2612 {
2613 if ( !ASMMemIsAllU8(&uBuf.ab[cbActualMin], sizeof(uBuf) - cbActualMin, 0xfe)
2614 && enmClass != FileStorageReserveIdInformation /* NTFS bug? */ )
2615 RTTestIFailed("%s/%#x/%c: Touched memory beyond returned length (cbActualMin=%#x, rcNt=%#x)",
2616 pszClass, cbBuf, chType, cbActualMin, rcNt);
2617 if (pcbName && *pcbName != cbName)
2618 RTTestIFailed("%s/%#x/%c: Wrong name length: %#x, expected %#x",
2619 pszClass, cbBuf, chType, *pcbName, cbName);
2620 if (pcbName && memcmp(abNameCopy, &uBuf.ab[offName], cbName) != 0)
2621 RTTestIFailed("%s/%#x/%c: Wrong name: %.*Rhxs",
2622 pszClass, cbBuf, chType, cbName, &uBuf.ab[offName]);
2623 }
2624 }
2625 }
2626 }
2627 else
2628 {
2629 if (!g_aNtQueryVolInfoFileClasses[i].fQuery)
2630 {
2631 if (rcNt != STATUS_INVALID_INFO_CLASS)
2632 RTTestIFailed("%s/%#x/%c: %#x, expected STATUS_INVALID_INFO_CLASS", pszClass, cbBuf, chType, rcNt);
2633 }
2634 else if ( rcNt != STATUS_INVALID_INFO_CLASS
2635 && rcNt != STATUS_INVALID_PARAMETER
2636 && !(rcNt == STATUS_ACCESS_DENIED && enmClass == FileFsControlInformation /* RDR2/W10 */)
2637 && !(rcNt == STATUS_OBJECT_NAME_NOT_FOUND && enmClass == FileFsObjectIdInformation /* RDR2/W10 */)
2638 )
2639 RTTestIFailed("%s/%#x/%c: %#x", pszClass, cbBuf, chType, rcNt);
2640 if ( (Ios.Status != VirginIos.Status || Ios.Information != VirginIos.Information)
2641 && !( Ios.Status == 0 && Ios.Information == 0
2642 && fType == RTFS_TYPE_DIRECTORY
2643 && ( enmClass == FileFsObjectIdInformation /* RDR2+NTFS on W10 */
2644 || enmClass == FileFsControlInformation /* RDR2 on W10 */
2645 || enmClass == FileFsVolumeFlagsInformation /* RDR2+NTFS on W10 */
2646 || enmClass == FileFsDataCopyInformation /* RDR2 on W10 */
2647 || enmClass == FileFsMetadataSizeInformation /* RDR2+NTFS on W10 */
2648 || enmClass == FileFsFullSizeInformationEx /* RDR2 on W10 */
2649 ) )
2650 )
2651 RTTestIFailed("%s/%#x/%c: I/O status block was modified: %#x %#zx (rcNt=%#x)",
2652 pszClass, cbBuf, chType, Ios.Status, Ios.Information, rcNt);
2653 if (!ASMMemIsAllU8(&uBuf, sizeof(uBuf), 0xff))
2654 RTTestIFailed("%s/%#x/%c: Buffer was touched in failure case!", pszClass, cbBuf, chType);
2655 }
2656 }
2657 RT_NOREF(fType);
2658}
2659
2660void fsPerfNtQueryVolInfoFile(void)
2661{
2662 RTTestISub("NtQueryVolumeInformationFile");
2663
2664 /* On a regular file: */
2665 RTFILE hFile1;
2666 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file2qvif")),
2667 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
2668 fsPerfNtQueryVolInfoFileWorker((HANDLE)RTFileToNative(hFile1), RTFS_TYPE_FILE);
2669 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2670
2671 /* On a directory: */
2672 HANDLE hDir1 = INVALID_HANDLE_VALUE;
2673 RTTESTI_CHECK_RC_RETV(RTNtPathOpenDir(InDir(RT_STR_TUPLE("")), GENERIC_READ | SYNCHRONIZE | FILE_SYNCHRONOUS_IO_NONALERT,
2674 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
2675 FILE_OPEN, 0, &hDir1, NULL), VINF_SUCCESS);
2676 fsPerfNtQueryVolInfoFileWorker(hDir1, RTFS_TYPE_DIRECTORY);
2677 RTTESTI_CHECK(CloseHandle(hDir1) == TRUE);
2678
2679 /* On a regular file opened for reading: */
2680 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file2qvif")),
2681 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
2682 fsPerfNtQueryVolInfoFileWorker((HANDLE)RTFileToNative(hFile1), RTFS_TYPE_FILE);
2683 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2684}
2685
2686#endif /* RT_OS_WINDOWS */
2687
2688void fsPerfFChMod(void)
2689{
2690 RTTestISub("fchmod");
2691 RTFILE hFile1;
2692 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file4")),
2693 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2694 RTFSOBJINFO ObjInfo = {0};
2695 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
2696 RTFMODE const fEvenMode = (ObjInfo.Attr.fMode & ~RTFS_UNIX_ALL_ACCESS_PERMS) | RTFS_DOS_READONLY | 0400;
2697 RTFMODE const fOddMode = (ObjInfo.Attr.fMode & ~(RTFS_UNIX_ALL_ACCESS_PERMS | RTFS_DOS_READONLY)) | 0640;
2698 PROFILE_FN(RTFileSetMode(hFile1, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTFileSetMode");
2699
2700 RTFileSetMode(hFile1, ObjInfo.Attr.fMode);
2701 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2702}
2703
2704
2705void fsPerfFUtimes(void)
2706{
2707 RTTestISub("futimes");
2708 RTFILE hFile1;
2709 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file5")),
2710 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2711 RTTIMESPEC Time1;
2712 RTTimeNow(&Time1);
2713 RTTIMESPEC Time2 = Time1;
2714 RTTimeSpecSubSeconds(&Time2, 3636);
2715
2716 RTFSOBJINFO ObjInfo0 = {0};
2717 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo0, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
2718
2719 /* Modify modification time: */
2720 RTTESTI_CHECK_RC(RTFileSetTimes(hFile1, NULL, &Time2, NULL, NULL), VINF_SUCCESS);
2721 RTFSOBJINFO ObjInfo1 = {0};
2722 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo1, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
2723 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo1.ModificationTime) >> 2) == (RTTimeSpecGetSeconds(&Time2) >> 2));
2724 char sz1[RTTIME_STR_LEN], sz2[RTTIME_STR_LEN]; /* Div by 1000 here for posix impl. using timeval. */
2725 RTTESTI_CHECK_MSG(RTTimeSpecGetNano(&ObjInfo1.AccessTime) / 1000 == RTTimeSpecGetNano(&ObjInfo0.AccessTime) / 1000,
2726 ("%s, expected %s", RTTimeSpecToString(&ObjInfo1.AccessTime, sz1, sizeof(sz1)),
2727 RTTimeSpecToString(&ObjInfo0.AccessTime, sz2, sizeof(sz2))));
2728
2729 /* Modify access time: */
2730 RTTESTI_CHECK_RC(RTFileSetTimes(hFile1, &Time1, NULL, NULL, NULL), VINF_SUCCESS);
2731 RTFSOBJINFO ObjInfo2 = {0};
2732 RTTESTI_CHECK_RC(RTFileQueryInfo(hFile1, &ObjInfo2, RTFSOBJATTRADD_NOTHING), VINF_SUCCESS);
2733 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo2.AccessTime) >> 2) == (RTTimeSpecGetSeconds(&Time1) >> 2));
2734 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo2.ModificationTime) / 1000 == RTTimeSpecGetNano(&ObjInfo1.ModificationTime) / 1000);
2735
2736 /* Benchmark it: */
2737 PROFILE_FN(RTFileSetTimes(hFile1, NULL, iIteration & 1 ? &Time1 : &Time2, NULL, NULL), g_nsTestRun, "RTFileSetTimes");
2738
2739 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2740}
2741
2742
2743void fsPerfStat(void)
2744{
2745 RTTestISub("stat");
2746 RTFSOBJINFO ObjInfo;
2747
2748 /* Non-existing files. */
2749 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InEmptyDir(RT_STR_TUPLE("no-such-file")),
2750 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VERR_FILE_NOT_FOUND);
2751 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
2752 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), FSPERF_VERR_PATH_NOT_FOUND);
2753 RTTESTI_CHECK_RC(RTPathQueryInfoEx(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
2754 &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VERR_PATH_NOT_FOUND);
2755
2756 /* Shallow: */
2757 RTFILE hFile1;
2758 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file3")),
2759 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2760 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2761
2762 PROFILE_FN(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), g_nsTestRun,
2763 "RTPathQueryInfoEx/NOTHING");
2764 PROFILE_FN(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK), g_nsTestRun,
2765 "RTPathQueryInfoEx/UNIX");
2766
2767
2768 /* Deep: */
2769 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file3")),
2770 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2771 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2772
2773 PROFILE_FN(RTPathQueryInfoEx(g_szDeepDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), g_nsTestRun,
2774 "RTPathQueryInfoEx/deep/NOTHING");
2775 PROFILE_FN(RTPathQueryInfoEx(g_szDeepDir, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK), g_nsTestRun,
2776 "RTPathQueryInfoEx/deep/UNIX");
2777
2778 /* Manytree: */
2779 char szPath[FSPERF_MAX_PATH];
2780 PROFILE_MANYTREE_FN(szPath, RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK),
2781 1, g_nsTestRun, "RTPathQueryInfoEx/manytree/NOTHING");
2782 PROFILE_MANYTREE_FN(szPath, RTPathQueryInfoEx(szPath, &ObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_ON_LINK),
2783 1, g_nsTestRun, "RTPathQueryInfoEx/manytree/UNIX");
2784}
2785
2786
2787void fsPerfChmod(void)
2788{
2789 RTTestISub("chmod");
2790
2791 /* Non-existing files. */
2792 RTTESTI_CHECK_RC(RTPathSetMode(InEmptyDir(RT_STR_TUPLE("no-such-file")), 0665),
2793 VERR_FILE_NOT_FOUND);
2794 RTTESTI_CHECK_RC(RTPathSetMode(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), 0665),
2795 FSPERF_VERR_PATH_NOT_FOUND);
2796 RTTESTI_CHECK_RC(RTPathSetMode(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), 0665), VERR_PATH_NOT_FOUND);
2797
2798 /* Shallow: */
2799 RTFILE hFile1;
2800 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file14")),
2801 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2802 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2803
2804 RTFSOBJINFO ObjInfo;
2805 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
2806 RTFMODE const fEvenMode = (ObjInfo.Attr.fMode & ~RTFS_UNIX_ALL_ACCESS_PERMS) | RTFS_DOS_READONLY | 0400;
2807 RTFMODE const fOddMode = (ObjInfo.Attr.fMode & ~(RTFS_UNIX_ALL_ACCESS_PERMS | RTFS_DOS_READONLY)) | 0640;
2808 PROFILE_FN(RTPathSetMode(g_szDir, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTPathSetMode");
2809 RTPathSetMode(g_szDir, ObjInfo.Attr.fMode);
2810
2811 /* Deep: */
2812 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file14")),
2813 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2814 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2815
2816 PROFILE_FN(RTPathSetMode(g_szDeepDir, iIteration & 1 ? fOddMode : fEvenMode), g_nsTestRun, "RTPathSetMode/deep");
2817 RTPathSetMode(g_szDeepDir, ObjInfo.Attr.fMode);
2818
2819 /* Manytree: */
2820 char szPath[FSPERF_MAX_PATH];
2821 PROFILE_MANYTREE_FN(szPath, RTPathSetMode(szPath, iIteration & 1 ? fOddMode : fEvenMode), 1, g_nsTestRun,
2822 "RTPathSetMode/manytree");
2823 DO_MANYTREE_FN(szPath, RTPathSetMode(szPath, ObjInfo.Attr.fMode));
2824}
2825
2826
2827void fsPerfUtimes(void)
2828{
2829 RTTestISub("utimes");
2830
2831 RTTIMESPEC Time1;
2832 RTTimeNow(&Time1);
2833 RTTIMESPEC Time2 = Time1;
2834 RTTimeSpecSubSeconds(&Time2, 3636);
2835
2836 /* Non-existing files. */
2837 RTTESTI_CHECK_RC(RTPathSetTimesEx(InEmptyDir(RT_STR_TUPLE("no-such-file")), NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
2838 VERR_FILE_NOT_FOUND);
2839 RTTESTI_CHECK_RC(RTPathSetTimesEx(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
2840 NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
2841 FSPERF_VERR_PATH_NOT_FOUND);
2842 RTTESTI_CHECK_RC(RTPathSetTimesEx(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
2843 NULL, &Time1, NULL, NULL, RTPATH_F_ON_LINK),
2844 VERR_PATH_NOT_FOUND);
2845
2846 /* Shallow: */
2847 RTFILE hFile1;
2848 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file15")),
2849 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2850 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2851
2852 RTFSOBJINFO ObjInfo0 = {0};
2853 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo0, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
2854
2855 /* Modify modification time: */
2856 RTTESTI_CHECK_RC(RTPathSetTimesEx(g_szDir, NULL, &Time2, NULL, NULL, RTPATH_F_ON_LINK), VINF_SUCCESS);
2857 RTFSOBJINFO ObjInfo1;
2858 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo1, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
2859 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo1.ModificationTime) >> 2) == (RTTimeSpecGetSeconds(&Time2) >> 2));
2860 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo1.AccessTime) / 1000 == RTTimeSpecGetNano(&ObjInfo0.AccessTime) / 1000 /* posix timeval */);
2861
2862 /* Modify access time: */
2863 RTTESTI_CHECK_RC(RTPathSetTimesEx(g_szDir, &Time1, NULL, NULL, NULL, RTPATH_F_ON_LINK), VINF_SUCCESS);
2864 RTFSOBJINFO ObjInfo2 = {0};
2865 RTTESTI_CHECK_RC(RTPathQueryInfoEx(g_szDir, &ObjInfo2, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK), VINF_SUCCESS);
2866 RTTESTI_CHECK((RTTimeSpecGetSeconds(&ObjInfo2.AccessTime) >> 2) == (RTTimeSpecGetSeconds(&Time1) >> 2));
2867 RTTESTI_CHECK(RTTimeSpecGetNano(&ObjInfo2.ModificationTime) / 1000 == RTTimeSpecGetNano(&ObjInfo1.ModificationTime) / 1000 /* posix timeval */);
2868
2869 /* Profile shallow: */
2870 PROFILE_FN(RTPathSetTimesEx(g_szDir, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
2871 NULL, NULL, RTPATH_F_ON_LINK),
2872 g_nsTestRun, "RTPathSetTimesEx");
2873
2874 /* Deep: */
2875 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file15")),
2876 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2877 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2878
2879 PROFILE_FN(RTPathSetTimesEx(g_szDeepDir, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
2880 NULL, NULL, RTPATH_F_ON_LINK),
2881 g_nsTestRun, "RTPathSetTimesEx/deep");
2882
2883 /* Manytree: */
2884 char szPath[FSPERF_MAX_PATH];
2885 PROFILE_MANYTREE_FN(szPath, RTPathSetTimesEx(szPath, iIteration & 1 ? &Time1 : &Time2, iIteration & 1 ? &Time2 : &Time1,
2886 NULL, NULL, RTPATH_F_ON_LINK),
2887 1, g_nsTestRun, "RTPathSetTimesEx/manytree");
2888}
2889
2890
2891DECL_FORCE_INLINE(int) fsPerfRenameMany(const char *pszFile, uint32_t iIteration)
2892{
2893 char szRenamed[FSPERF_MAX_PATH];
2894 strcat(strcpy(szRenamed, pszFile), "-renamed");
2895 if (!(iIteration & 1))
2896 return RTPathRename(pszFile, szRenamed, 0);
2897 return RTPathRename(szRenamed, pszFile, 0);
2898}
2899
2900
2901void fsPerfRename(void)
2902{
2903 RTTestISub("rename");
2904 char szPath[FSPERF_MAX_PATH];
2905
2906/** @todo rename directories too! */
2907/** @todo check overwriting files and directoris (empty ones should work on
2908 * unix). */
2909
2910 /* Non-existing files. */
2911 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("other-no-such-file")));
2912 RTTESTI_CHECK_RC(RTPathRename(InEmptyDir(RT_STR_TUPLE("no-such-file")), szPath, 0), VERR_FILE_NOT_FOUND);
2913 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "other-no-such-file")));
2914 RTTESTI_CHECK_RC(RTPathRename(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), szPath, 0),
2915 FSPERF_VERR_PATH_NOT_FOUND);
2916 strcpy(szPath, InEmptyDir(RT_STR_TUPLE("other-no-such-file")));
2917 RTTESTI_CHECK_RC(RTPathRename(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), szPath, 0), VERR_PATH_NOT_FOUND);
2918
2919 RTFILE hFile1;
2920 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file16")),
2921 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2922 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2923 strcat(strcpy(szPath, g_szDir), "-no-such-dir" RTPATH_SLASH_STR "file16");
2924 RTTESTI_CHECK_RC(RTPathRename(szPath, g_szDir, 0), FSPERF_VERR_PATH_NOT_FOUND);
2925 RTTESTI_CHECK_RC(RTPathRename(g_szDir, szPath, 0), FSPERF_VERR_PATH_NOT_FOUND);
2926
2927 /* Shallow: */
2928 strcat(strcpy(szPath, g_szDir), "-other");
2929 PROFILE_FN(RTPathRename(iIteration & 1 ? szPath : g_szDir, iIteration & 1 ? g_szDir : szPath, 0), g_nsTestRun, "RTPathRename");
2930
2931 /* Deep: */
2932 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDeepDir(RT_STR_TUPLE("file15")),
2933 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
2934 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
2935
2936 strcat(strcpy(szPath, g_szDeepDir), "-other");
2937 PROFILE_FN(RTPathRename(iIteration & 1 ? szPath : g_szDeepDir, iIteration & 1 ? g_szDeepDir : szPath, 0),
2938 g_nsTestRun, "RTPathRename/deep");
2939
2940 /* Manytree: */
2941 PROFILE_MANYTREE_FN(szPath, fsPerfRenameMany(szPath, iIteration), 2, g_nsTestRun, "RTPathRename/manytree");
2942}
2943
2944
2945/**
2946 * Wrapper around RTDirOpen/RTDirOpenFiltered which takes g_fRelativeDir into
2947 * account.
2948 */
2949DECL_FORCE_INLINE(int) fsPerfOpenDirWrap(PRTDIR phDir, const char *pszPath)
2950{
2951 if (!g_fRelativeDir)
2952 return RTDirOpen(phDir, pszPath);
2953 return RTDirOpenFiltered(phDir, pszPath, RTDIRFILTER_NONE, RTDIR_F_NO_ABS_PATH);
2954}
2955
2956
2957DECL_FORCE_INLINE(int) fsPerfOpenClose(const char *pszDir)
2958{
2959 RTDIR hDir;
2960 RTTESTI_CHECK_RC_RET(fsPerfOpenDirWrap(&hDir, pszDir), VINF_SUCCESS, rcCheck);
2961 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
2962 return VINF_SUCCESS;
2963}
2964
2965
2966void vsPerfDirOpen(void)
2967{
2968 RTTestISub("dir open");
2969 RTDIR hDir;
2970
2971 /*
2972 * Non-existing files.
2973 */
2974 RTTESTI_CHECK_RC(fsPerfOpenDirWrap(&hDir, InEmptyDir(RT_STR_TUPLE("no-such-file"))), VERR_FILE_NOT_FOUND);
2975 RTTESTI_CHECK_RC(fsPerfOpenDirWrap(&hDir, InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
2976 RTTESTI_CHECK_RC(fsPerfOpenDirWrap(&hDir, InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
2977
2978 /*
2979 * Check that open + close works.
2980 */
2981 g_szEmptyDir[g_cchEmptyDir] = '\0';
2982 RTTESTI_CHECK_RC_RETV(fsPerfOpenDirWrap(&hDir, g_szEmptyDir), VINF_SUCCESS);
2983 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
2984
2985
2986 /*
2987 * Profile empty dir and dir with many files.
2988 */
2989 g_szEmptyDir[g_cchEmptyDir] = '\0';
2990 PROFILE_FN(fsPerfOpenClose(g_szEmptyDir), g_nsTestRun, "RTDirOpen/Close empty");
2991 if (g_fManyFiles)
2992 {
2993 InDir(RT_STR_TUPLE("manyfiles"));
2994 PROFILE_FN(fsPerfOpenClose(g_szDir), g_nsTestRun, "RTDirOpen/Close manyfiles");
2995 }
2996}
2997
2998
2999DECL_FORCE_INLINE(int) fsPerfEnumEmpty(void)
3000{
3001 RTDIR hDir;
3002 g_szEmptyDir[g_cchEmptyDir] = '\0';
3003 RTTESTI_CHECK_RC_RET(fsPerfOpenDirWrap(&hDir, g_szEmptyDir), VINF_SUCCESS, rcCheck);
3004
3005 RTDIRENTRY Entry;
3006 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
3007 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
3008 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
3009
3010 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
3011 return VINF_SUCCESS;
3012}
3013
3014
3015DECL_FORCE_INLINE(int) fsPerfEnumManyFiles(void)
3016{
3017 RTDIR hDir;
3018 RTTESTI_CHECK_RC_RET(fsPerfOpenDirWrap(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS, rcCheck);
3019 uint32_t cLeft = g_cManyFiles + 2;
3020 for (;;)
3021 {
3022 RTDIRENTRY Entry;
3023 if (cLeft > 0)
3024 RTTESTI_CHECK_RC_BREAK(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
3025 else
3026 {
3027 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
3028 break;
3029 }
3030 cLeft--;
3031 }
3032 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
3033 return VINF_SUCCESS;
3034}
3035
3036
3037void vsPerfDirEnum(void)
3038{
3039 RTTestISub("dir enum");
3040 RTDIR hDir;
3041
3042 /*
3043 * The empty directory.
3044 */
3045 g_szEmptyDir[g_cchEmptyDir] = '\0';
3046 RTTESTI_CHECK_RC_RETV(fsPerfOpenDirWrap(&hDir, g_szEmptyDir), VINF_SUCCESS);
3047
3048 uint32_t fDots = 0;
3049 RTDIRENTRY Entry;
3050 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
3051 RTTESTI_CHECK(RTDirEntryIsStdDotLink(&Entry));
3052 fDots |= RT_BIT_32(Entry.cbName - 1);
3053
3054 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VINF_SUCCESS);
3055 RTTESTI_CHECK(RTDirEntryIsStdDotLink(&Entry));
3056 fDots |= RT_BIT_32(Entry.cbName - 1);
3057 RTTESTI_CHECK(fDots == 3);
3058
3059 RTTESTI_CHECK_RC(RTDirRead(hDir, &Entry, NULL), VERR_NO_MORE_FILES);
3060
3061 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
3062
3063 /*
3064 * The directory with many files in it.
3065 */
3066 if (g_fManyFiles)
3067 {
3068 fDots = 0;
3069 uint32_t const cBitmap = RT_ALIGN_32(g_cManyFiles, 64);
3070 void *pvBitmap = alloca(cBitmap / 8);
3071 RT_BZERO(pvBitmap, cBitmap / 8);
3072 for (uint32_t i = g_cManyFiles; i < cBitmap; i++)
3073 ASMBitSet(pvBitmap, i);
3074
3075 uint32_t cFiles = 0;
3076 RTTESTI_CHECK_RC_RETV(fsPerfOpenDirWrap(&hDir, InDir(RT_STR_TUPLE("manyfiles"))), VINF_SUCCESS);
3077 for (;;)
3078 {
3079 int rc = RTDirRead(hDir, &Entry, NULL);
3080 if (rc == VINF_SUCCESS)
3081 {
3082 if (Entry.szName[0] == '.')
3083 {
3084 if (Entry.szName[1] == '.')
3085 {
3086 RTTESTI_CHECK(!(fDots & 2));
3087 fDots |= 2;
3088 }
3089 else
3090 {
3091 RTTESTI_CHECK(Entry.szName[1] == '\0');
3092 RTTESTI_CHECK(!(fDots & 1));
3093 fDots |= 1;
3094 }
3095 }
3096 else
3097 {
3098 uint32_t iFile = UINT32_MAX;
3099 RTTESTI_CHECK_RC(RTStrToUInt32Full(Entry.szName, 10, &iFile), VINF_SUCCESS);
3100 if ( iFile < g_cManyFiles
3101 && !ASMBitTest(pvBitmap, iFile))
3102 {
3103 ASMBitSet(pvBitmap, iFile);
3104 cFiles++;
3105 }
3106 else
3107 RTTestFailed(g_hTest, "line %u: iFile=%u g_cManyFiles=%u\n", __LINE__, iFile, g_cManyFiles);
3108 }
3109 }
3110 else if (rc == VERR_NO_MORE_FILES)
3111 break;
3112 else
3113 {
3114 RTTestFailed(g_hTest, "RTDirRead failed enumerating manyfiles: %Rrc\n", rc);
3115 RTDirClose(hDir);
3116 return;
3117 }
3118 }
3119 RTTESTI_CHECK_RC(RTDirClose(hDir), VINF_SUCCESS);
3120 RTTESTI_CHECK(fDots == 3);
3121 RTTESTI_CHECK(cFiles == g_cManyFiles);
3122 RTTESTI_CHECK(ASMMemIsAllU8(pvBitmap, cBitmap / 8, 0xff));
3123 }
3124
3125 /*
3126 * Profile.
3127 */
3128 PROFILE_FN(fsPerfEnumEmpty(),g_nsTestRun, "RTDirOpen/Read/Close empty");
3129 if (g_fManyFiles)
3130 PROFILE_FN(fsPerfEnumManyFiles(), g_nsTestRun, "RTDirOpen/Read/Close manyfiles");
3131}
3132
3133
3134void fsPerfMkRmDir(void)
3135{
3136 RTTestISub("mkdir/rmdir");
3137
3138 /* Non-existing directories: */
3139 RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir"))), VERR_FILE_NOT_FOUND);
3140 RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR))), VERR_FILE_NOT_FOUND);
3141 RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
3142 RTTESTI_CHECK_RC(RTDirRemove(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file" RTPATH_SLASH_STR))), FSPERF_VERR_PATH_NOT_FOUND);
3143 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
3144 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file" RTPATH_SLASH_STR))), VERR_PATH_NOT_FOUND);
3145
3146 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")), 0755, 0), FSPERF_VERR_PATH_NOT_FOUND);
3147 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")), 0755, 0), VERR_PATH_NOT_FOUND);
3148
3149 /* Already existing directories and files: */
3150 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE(".")), 0755, 0), VERR_ALREADY_EXISTS);
3151 RTTESTI_CHECK_RC(RTDirCreate(InEmptyDir(RT_STR_TUPLE("..")), 0755, 0), VERR_ALREADY_EXISTS);
3152
3153 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("known-file"))), VERR_NOT_A_DIRECTORY);
3154 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR))), VERR_NOT_A_DIRECTORY);
3155
3156 /* Remove directory with subdirectories: */
3157#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
3158 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("."))), VERR_DIR_NOT_EMPTY);
3159#else
3160 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE("."))), VERR_INVALID_PARAMETER); /* EINVAL for '.' */
3161#endif
3162#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
3163 int rc = RTDirRemove(InDir(RT_STR_TUPLE("..")));
3164# ifdef RT_OS_WINDOWS
3165 if (rc != VERR_DIR_NOT_EMPTY /*ntfs root*/ && rc != VERR_SHARING_VIOLATION /*ntfs weird*/ && rc != VERR_ACCESS_DENIED /*fat32 root*/)
3166 RTTestIFailed("RTDirRemove(%s) -> %Rrc, expected VERR_DIR_NOT_EMPTY, VERR_SHARING_VIOLATION or VERR_ACCESS_DENIED", g_szDir, rc);
3167# else
3168 if (rc != VERR_DIR_NOT_EMPTY && rc != VERR_RESOURCE_BUSY /*IPRT/kLIBC fun*/)
3169 RTTestIFailed("RTDirRemove(%s) -> %Rrc, expected VERR_DIR_NOT_EMPTY or VERR_RESOURCE_BUSY", g_szDir, rc);
3170
3171 APIRET orc;
3172 RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE(".")))) == ERROR_ACCESS_DENIED,
3173 ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_ACCESS_DENIED));
3174 RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE("..")))) == ERROR_ACCESS_DENIED,
3175 ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_ACCESS_DENIED));
3176 RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE("")))) == ERROR_PATH_NOT_FOUND, /* a little weird (fsrouter) */
3177 ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_PATH_NOT_FOUND));
3178
3179# endif
3180#else
3181 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(".."))), VERR_DIR_NOT_EMPTY);
3182#endif
3183 RTTESTI_CHECK_RC(RTDirRemove(InDir(RT_STR_TUPLE(""))), VERR_DIR_NOT_EMPTY);
3184
3185 /* Create a directory and remove it: */
3186 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("subdir-1")), 0755, 0), VINF_SUCCESS);
3187 RTTESTI_CHECK_RC(RTDirRemove(g_szDir), VINF_SUCCESS);
3188
3189 /* Create a file and try remove it or create a directory with the same name: */
3190 RTFILE hFile1;
3191 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file18")),
3192 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
3193 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
3194 RTTESTI_CHECK_RC(RTDirRemove(g_szDir), VERR_NOT_A_DIRECTORY);
3195 RTTESTI_CHECK_RC(RTDirCreate(g_szDir, 0755, 0), VERR_ALREADY_EXISTS);
3196 RTTESTI_CHECK_RC(RTDirCreate(InDir(RT_STR_TUPLE("file18" RTPATH_SLASH_STR "subdir")), 0755, 0), VERR_PATH_NOT_FOUND);
3197
3198 /*
3199 * Profile alternately creating and removing a bunch of directories.
3200 */
3201 RTTESTI_CHECK_RC_RETV(RTDirCreate(InDir(RT_STR_TUPLE("subdir-2")), 0755, 0), VINF_SUCCESS);
3202 size_t cchDir = strlen(g_szDir);
3203 g_szDir[cchDir++] = RTPATH_SLASH;
3204 g_szDir[cchDir++] = 's';
3205
3206 uint32_t cCreated = 0;
3207 uint64_t nsCreate = 0;
3208 uint64_t nsRemove = 0;
3209 for (;;)
3210 {
3211 /* Create a bunch: */
3212 uint64_t nsStart = RTTimeNanoTS();
3213 for (uint32_t i = 0; i < 998; i++)
3214 {
3215 RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD);
3216 RTTESTI_CHECK_RC_RETV(RTDirCreate(g_szDir, 0755, 0), VINF_SUCCESS);
3217 }
3218 nsCreate += RTTimeNanoTS() - nsStart;
3219 cCreated += 998;
3220
3221 /* Remove the bunch: */
3222 nsStart = RTTimeNanoTS();
3223 for (uint32_t i = 0; i < 998; i++)
3224 {
3225 RTStrFormatU32(&g_szDir[cchDir], sizeof(g_szDir) - cchDir, i, 10, 3, 3, RTSTR_F_ZEROPAD);
3226 RTTESTI_CHECK_RC_RETV(RTDirRemove(g_szDir), VINF_SUCCESS);
3227 }
3228 nsRemove = RTTimeNanoTS() - nsStart;
3229
3230 /* Check if we got time for another round: */
3231 if ( ( nsRemove >= g_nsTestRun
3232 && nsCreate >= g_nsTestRun)
3233 || nsCreate + nsRemove >= g_nsTestRun * 3)
3234 break;
3235 }
3236 RTTestIValue("RTDirCreate", nsCreate / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE);
3237 RTTestIValue("RTDirRemove", nsRemove / cCreated, RTTESTUNIT_NS_PER_OCCURRENCE);
3238}
3239
3240
3241void fsPerfStatVfs(void)
3242{
3243 RTTestISub("statvfs");
3244
3245 g_szEmptyDir[g_cchEmptyDir] = '\0';
3246 RTFOFF cbTotal;
3247 RTFOFF cbFree;
3248 uint32_t cbBlock;
3249 uint32_t cbSector;
3250 RTTESTI_CHECK_RC(RTFsQuerySizes(g_szEmptyDir, &cbTotal, &cbFree, &cbBlock, &cbSector), VINF_SUCCESS);
3251
3252 uint32_t uSerial;
3253 RTTESTI_CHECK_RC(RTFsQuerySerial(g_szEmptyDir, &uSerial), VINF_SUCCESS);
3254
3255 RTFSPROPERTIES Props;
3256 RTTESTI_CHECK_RC(RTFsQueryProperties(g_szEmptyDir, &Props), VINF_SUCCESS);
3257
3258 RTFSTYPE enmType;
3259 RTTESTI_CHECK_RC(RTFsQueryType(g_szEmptyDir, &enmType), VINF_SUCCESS);
3260
3261 g_szDeepDir[g_cchDeepDir] = '\0';
3262 PROFILE_FN(RTFsQuerySizes(g_szEmptyDir, &cbTotal, &cbFree, &cbBlock, &cbSector), g_nsTestRun, "RTFsQuerySize/empty");
3263 PROFILE_FN(RTFsQuerySizes(g_szDeepDir, &cbTotal, &cbFree, &cbBlock, &cbSector), g_nsTestRun, "RTFsQuerySize/deep");
3264}
3265
3266
3267void fsPerfRm(void)
3268{
3269 RTTestISub("rm");
3270
3271 /* Non-existing files. */
3272 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-file"))), VERR_FILE_NOT_FOUND);
3273 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-file" RTPATH_SLASH_STR))), VERR_FILE_NOT_FOUND);
3274 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
3275 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file" RTPATH_SLASH_STR))), FSPERF_VERR_PATH_NOT_FOUND);
3276 RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
3277 RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file" RTPATH_SLASH_STR))), VERR_PATH_NOT_FOUND);
3278
3279 /* Existing file but specified as if it was a directory: */
3280#if defined(RT_OS_WINDOWS)
3281 RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR ))), VERR_INVALID_NAME);
3282#else
3283 RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR))), VERR_NOT_A_FILE); //??
3284#endif
3285
3286 /* Directories: */
3287#if defined(RT_OS_WINDOWS)
3288 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("."))), VERR_ACCESS_DENIED);
3289 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(".."))), VERR_ACCESS_DENIED);
3290 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(""))), VERR_ACCESS_DENIED);
3291#elif defined(RT_OS_DARWIN) /* unlink() on xnu 16.7.0 is behaviour totally werid: */
3292 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("."))), VERR_INVALID_PARAMETER);
3293 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(".."))), VINF_SUCCESS /*WTF?!?*/);
3294 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(""))), VERR_ACCESS_DENIED);
3295#elif defined(RT_OS_OS2) /* OS/2 has a busted unlink, it think it should remove directories too. */
3296 RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE("."))), VERR_DIR_NOT_EMPTY);
3297 int rc = RTFileDelete(InDir(RT_STR_TUPLE("..")));
3298 if (rc != VERR_DIR_NOT_EMPTY && rc != VERR_FILE_NOT_FOUND && rc != VERR_RESOURCE_BUSY)
3299 RTTestIFailed("RTFileDelete(%s) -> %Rrc, expected VERR_DIR_NOT_EMPTY or VERR_FILE_NOT_FOUND or VERR_RESOURCE_BUSY", g_szDir, rc);
3300 RTTESTI_CHECK_RC(RTFileDelete(InDir(RT_STR_TUPLE(""))), VERR_DIR_NOT_EMPTY);
3301 APIRET orc;
3302 RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE(".")))) == ERROR_ACCESS_DENIED,
3303 ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_ACCESS_DENIED));
3304 RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE("..")))) == ERROR_ACCESS_DENIED,
3305 ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_ACCESS_DENIED));
3306 RTTESTI_CHECK_MSG((orc = DosDelete((PCSZ)InEmptyDir(RT_STR_TUPLE("")))) == ERROR_PATH_NOT_FOUND,
3307 ("DosDelete(%s) -> %u, expected %u\n", g_szEmptyDir, orc, ERROR_PATH_NOT_FOUND)); /* hpfs+jfs; weird. */
3308
3309#else
3310 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE("."))), VERR_IS_A_DIRECTORY);
3311 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(".."))), VERR_IS_A_DIRECTORY);
3312 RTTESTI_CHECK_RC(RTFileDelete(InEmptyDir(RT_STR_TUPLE(""))), VERR_IS_A_DIRECTORY);
3313#endif
3314
3315 /* Shallow: */
3316 RTFILE hFile1;
3317 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file19")),
3318 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
3319 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
3320 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
3321 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VERR_FILE_NOT_FOUND);
3322
3323 if (g_fManyFiles)
3324 {
3325 /*
3326 * Profile the deletion of the manyfiles content.
3327 */
3328 {
3329 InDir(RT_STR_TUPLE("manyfiles" RTPATH_SLASH_STR));
3330 size_t const offFilename = strlen(g_szDir);
3331 fsPerfYield();
3332 uint64_t const nsStart = RTTimeNanoTS();
3333 for (uint32_t i = 0; i < g_cManyFiles; i++)
3334 {
3335 RTStrFormatU32(&g_szDir[offFilename], sizeof(g_szDir) - offFilename, i, 10, 5, 5, RTSTR_F_ZEROPAD);
3336 RTTESTI_CHECK_RC_RETV(RTFileDelete(g_szDir), VINF_SUCCESS);
3337 }
3338 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
3339 RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Deleted %u empty files from a single directory", g_cManyFiles);
3340 RTTestIValueF(cNsElapsed / g_cManyFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Delete file (single dir)");
3341 }
3342
3343 /*
3344 * Ditto for the manytree.
3345 */
3346 {
3347 char szPath[FSPERF_MAX_PATH];
3348 uint64_t const nsStart = RTTimeNanoTS();
3349 DO_MANYTREE_FN(szPath, RTTESTI_CHECK_RC_RETV(RTFileDelete(szPath), VINF_SUCCESS));
3350 uint64_t const cNsElapsed = RTTimeNanoTS() - nsStart;
3351 RTTestIValueF(cNsElapsed, RTTESTUNIT_NS, "Deleted %u empty files in tree", g_cManyTreeFiles);
3352 RTTestIValueF(cNsElapsed / g_cManyTreeFiles, RTTESTUNIT_NS_PER_OCCURRENCE, "Delete file (tree)");
3353 }
3354 }
3355}
3356
3357
3358void fsPerfChSize(void)
3359{
3360 RTTestISub("chsize");
3361
3362 /*
3363 * We need some free space to perform this test.
3364 */
3365 g_szDir[g_cchDir] = '\0';
3366 RTFOFF cbFree = 0;
3367 RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
3368 if (cbFree < _1M)
3369 {
3370 RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 1MB", cbFree);
3371 return;
3372 }
3373
3374 /*
3375 * Create a file and play around with it's size.
3376 * We let the current file position follow the end position as we make changes.
3377 */
3378 RTFILE hFile1;
3379 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file20")),
3380 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
3381 uint64_t cbFile = UINT64_MAX;
3382 RTTESTI_CHECK_RC(RTFileGetSize(hFile1, &cbFile), VINF_SUCCESS);
3383 RTTESTI_CHECK(cbFile == 0);
3384
3385 uint8_t abBuf[4096];
3386 static uint64_t const s_acbChanges[] =
3387 {
3388 1023, 1024, 1024, 1025, 8192, 11111, _1M, _8M, _8M,
3389 _4M, _2M + 1, _1M - 1, 65537, 65536, 32768, 8000, 7999, 7998, 1024, 1, 0
3390 };
3391 uint64_t cbOld = 0;
3392 for (unsigned i = 0; i < RT_ELEMENTS(s_acbChanges); i++)
3393 {
3394 uint64_t cbNew = s_acbChanges[i];
3395 if (cbNew + _64K >= (uint64_t)cbFree)
3396 continue;
3397
3398 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, cbNew), VINF_SUCCESS);
3399 RTTESTI_CHECK_RC(RTFileGetSize(hFile1, &cbFile), VINF_SUCCESS);
3400 RTTESTI_CHECK_MSG(cbFile == cbNew, ("cbFile=%#RX64 cbNew=%#RX64\n", cbFile, cbNew));
3401
3402 if (cbNew > cbOld)
3403 {
3404 /* Check that the extension is all zeroed: */
3405 uint64_t cbLeft = cbNew - cbOld;
3406 while (cbLeft > 0)
3407 {
3408 memset(abBuf, 0xff, sizeof(abBuf));
3409 size_t cbToRead = sizeof(abBuf);
3410 if (cbToRead > cbLeft)
3411 cbToRead = (size_t)cbLeft;
3412 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, cbToRead, NULL), VINF_SUCCESS);
3413 RTTESTI_CHECK(ASMMemIsZero(abBuf, cbToRead));
3414 cbLeft -= cbToRead;
3415 }
3416 }
3417 else
3418 {
3419 /* Check that reading fails with EOF because current position is now beyond the end: */
3420 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 1, NULL), VERR_EOF);
3421
3422 /* Keep current position at the end of the file: */
3423 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbNew, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
3424 }
3425 cbOld = cbNew;
3426 }
3427
3428 /*
3429 * Profile just the file setting operation itself, keeping the changes within
3430 * an allocation unit to avoid needing to adjust the actual (host) FS allocation.
3431 * ASSUMES allocation unit >= 512 and power of two.
3432 */
3433 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, _64K), VINF_SUCCESS);
3434 PROFILE_FN(RTFileSetSize(hFile1, _64K - (iIteration & 255) - 128), g_nsTestRun, "RTFileSetSize/noalloc");
3435
3436 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS);
3437 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
3438 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
3439}
3440
3441
3442int fsPerfIoPrepFile(RTFILE hFile1, uint64_t cbFile, uint8_t **ppbFree)
3443{
3444 /*
3445 * Seek to the end - 4K and write the last 4K.
3446 * This should have the effect of filling the whole file with zeros.
3447 */
3448 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, cbFile - _4K, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
3449 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, g_abRTZero4K, _4K, NULL), VINF_SUCCESS, rcCheck);
3450
3451 /*
3452 * Check that the space we searched across actually is zero filled.
3453 */
3454 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
3455 size_t cbBuf = _1M;
3456 uint8_t *pbBuf = *ppbFree = (uint8_t *)RTMemAlloc(cbBuf);
3457 RTTESTI_CHECK_RET(pbBuf != NULL, VERR_NO_MEMORY);
3458 uint64_t cbLeft = cbFile;
3459 while (cbLeft > 0)
3460 {
3461 size_t cbToRead = cbBuf;
3462 if (cbToRead > cbLeft)
3463 cbToRead = (size_t)cbLeft;
3464 pbBuf[cbToRead] = 0xff;
3465
3466 RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBuf, cbToRead, NULL), VINF_SUCCESS, rcCheck);
3467 RTTESTI_CHECK_RET(ASMMemIsZero(pbBuf, cbToRead), VERR_MISMATCH);
3468
3469 cbLeft -= cbToRead;
3470 }
3471
3472 /*
3473 * Fill the file with 0xf6 and insert offset markers with 1KB intervals.
3474 */
3475 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
3476 memset(pbBuf, 0xf6, cbBuf);
3477 cbLeft = cbFile;
3478 uint64_t off = 0;
3479 while (cbLeft > 0)
3480 {
3481 Assert(!(off & (_1K - 1)));
3482 Assert(!(cbBuf & (_1K - 1)));
3483 for (size_t offBuf = 0; offBuf < cbBuf; offBuf += _1K, off += _1K)
3484 *(uint64_t *)&pbBuf[offBuf] = off;
3485
3486 size_t cbToWrite = cbBuf;
3487 if (cbToWrite > cbLeft)
3488 cbToWrite = (size_t)cbLeft;
3489
3490 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBuf, cbToWrite, NULL), VINF_SUCCESS, rcCheck);
3491
3492 cbLeft -= cbToWrite;
3493 }
3494
3495 return VINF_SUCCESS;
3496}
3497
3498
3499/**
3500 * Checks the content read from the file fsPerfIoPrepFile() prepared.
3501 */
3502bool fsPerfCheckReadBuf(unsigned uLineNo, uint64_t off, uint8_t const *pbBuf, size_t cbBuf, uint8_t bFiller = 0xf6)
3503{
3504 uint32_t cMismatches = 0;
3505 size_t offBuf = 0;
3506 uint32_t offBlock = (uint32_t)(off & (_1K - 1));
3507 while (offBuf < cbBuf)
3508 {
3509 /*
3510 * Check the offset marker:
3511 */
3512 if (offBlock < sizeof(uint64_t))
3513 {
3514 RTUINT64U uMarker;
3515 uMarker.u = off + offBuf - offBlock;
3516 unsigned offMarker = offBlock & (sizeof(uint64_t) - 1);
3517 while (offMarker < sizeof(uint64_t) && offBuf < cbBuf)
3518 {
3519 if (uMarker.au8[offMarker] != pbBuf[offBuf])
3520 {
3521 RTTestIFailed("%u: Mismatch at buffer/file offset %#zx/%#RX64: %#x, expected %#x",
3522 uLineNo, offBuf, off + offBuf, pbBuf[offBuf], uMarker.au8[offMarker]);
3523 if (cMismatches++ > 32)
3524 return false;
3525 }
3526 offMarker++;
3527 offBuf++;
3528 }
3529 offBlock = sizeof(uint64_t);
3530 }
3531
3532 /*
3533 * Check the filling:
3534 */
3535 size_t cbFilling = RT_MIN(_1K - offBlock, cbBuf - offBuf);
3536 if ( cbFilling == 0
3537 || ASMMemIsAllU8(&pbBuf[offBuf], cbFilling, bFiller))
3538 offBuf += cbFilling;
3539 else
3540 {
3541 /* Some mismatch, locate it/them: */
3542 while (cbFilling > 0 && offBuf < cbBuf)
3543 {
3544 if (pbBuf[offBuf] != bFiller)
3545 {
3546 RTTestIFailed("%u: Mismatch at buffer/file offset %#zx/%#RX64: %#x, expected %#04x",
3547 uLineNo, offBuf, off + offBuf, pbBuf[offBuf], bFiller);
3548 if (cMismatches++ > 32)
3549 return false;
3550 }
3551 offBuf++;
3552 cbFilling--;
3553 }
3554 }
3555 offBlock = 0;
3556 }
3557 return cMismatches == 0;
3558}
3559
3560
3561/**
3562 * Sets up write buffer with offset markers and fillers.
3563 */
3564void fsPerfFillWriteBuf(uint64_t off, uint8_t *pbBuf, size_t cbBuf, uint8_t bFiller = 0xf6)
3565{
3566 uint32_t offBlock = (uint32_t)(off & (_1K - 1));
3567 while (cbBuf > 0)
3568 {
3569 /* The marker. */
3570 if (offBlock < sizeof(uint64_t))
3571 {
3572 RTUINT64U uMarker;
3573 uMarker.u = off + offBlock;
3574 if (cbBuf > sizeof(uMarker) - offBlock)
3575 {
3576 memcpy(pbBuf, &uMarker.au8[offBlock], sizeof(uMarker) - offBlock);
3577 pbBuf += sizeof(uMarker) - offBlock;
3578 cbBuf -= sizeof(uMarker) - offBlock;
3579 off += sizeof(uMarker) - offBlock;
3580 }
3581 else
3582 {
3583 memcpy(pbBuf, &uMarker.au8[offBlock], cbBuf);
3584 return;
3585 }
3586 offBlock = sizeof(uint64_t);
3587 }
3588
3589 /* Do the filling. */
3590 size_t cbFilling = RT_MIN(_1K - offBlock, cbBuf);
3591 memset(pbBuf, bFiller, cbFilling);
3592 pbBuf += cbFilling;
3593 cbBuf -= cbFilling;
3594 off += cbFilling;
3595
3596 offBlock = 0;
3597 }
3598}
3599
3600
3601
3602void fsPerfIoSeek(RTFILE hFile1, uint64_t cbFile)
3603{
3604 /*
3605 * Do a bunch of search tests, most which are random.
3606 */
3607 struct
3608 {
3609 int rc;
3610 uint32_t uMethod;
3611 int64_t offSeek;
3612 uint64_t offActual;
3613
3614 } aSeeks[9 + 64] =
3615 {
3616 { VINF_SUCCESS, RTFILE_SEEK_BEGIN, 0, 0 },
3617 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 },
3618 { VINF_SUCCESS, RTFILE_SEEK_END, 0, cbFile },
3619 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -4096, cbFile - 4096 },
3620 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 4096 - (int64_t)cbFile, 0 },
3621 { VINF_SUCCESS, RTFILE_SEEK_END, -(int64_t)cbFile/2, cbFile / 2 + (cbFile & 1) },
3622 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, -(int64_t)cbFile/2, 0 },
3623#if defined(RT_OS_WINDOWS)
3624 { VERR_NEGATIVE_SEEK, RTFILE_SEEK_CURRENT, -1, 0 },
3625#else
3626 { VERR_INVALID_PARAMETER, RTFILE_SEEK_CURRENT, -1, 0 },
3627#endif
3628 { VINF_SUCCESS, RTFILE_SEEK_CURRENT, 0, 0 },
3629 };
3630
3631 uint64_t offActual = 0;
3632 for (unsigned i = 9; i < RT_ELEMENTS(aSeeks); i++)
3633 {
3634 switch (RTRandU32Ex(RTFILE_SEEK_BEGIN, RTFILE_SEEK_END))
3635 {
3636 default: AssertFailedBreak();
3637 case RTFILE_SEEK_BEGIN:
3638 aSeeks[i].uMethod = RTFILE_SEEK_BEGIN;
3639 aSeeks[i].rc = VINF_SUCCESS;
3640 aSeeks[i].offSeek = RTRandU64Ex(0, cbFile + cbFile / 8);
3641 aSeeks[i].offActual = offActual = aSeeks[i].offSeek;
3642 break;
3643
3644 case RTFILE_SEEK_CURRENT:
3645 aSeeks[i].uMethod = RTFILE_SEEK_CURRENT;
3646 aSeeks[i].rc = VINF_SUCCESS;
3647 aSeeks[i].offSeek = (int64_t)RTRandU64Ex(0, cbFile + cbFile / 8) - (int64_t)offActual;
3648 aSeeks[i].offActual = offActual += aSeeks[i].offSeek;
3649 break;
3650
3651 case RTFILE_SEEK_END:
3652 aSeeks[i].uMethod = RTFILE_SEEK_END;
3653 aSeeks[i].rc = VINF_SUCCESS;
3654 aSeeks[i].offSeek = -(int64_t)RTRandU64Ex(0, cbFile);
3655 aSeeks[i].offActual = offActual = cbFile + aSeeks[i].offSeek;
3656 break;
3657 }
3658 }
3659
3660 for (unsigned iDoReadCheck = 0; iDoReadCheck < 2; iDoReadCheck++)
3661 {
3662 for (uint32_t i = 0; i < RT_ELEMENTS(aSeeks); i++)
3663 {
3664 offActual = UINT64_MAX;
3665 int rc = RTFileSeek(hFile1, aSeeks[i].offSeek, aSeeks[i].uMethod, &offActual);
3666 if (rc != aSeeks[i].rc)
3667 RTTestIFailed("Seek #%u: Expected %Rrc, got %Rrc", i, aSeeks[i].rc, rc);
3668 if (RT_SUCCESS(rc) && offActual != aSeeks[i].offActual)
3669 RTTestIFailed("Seek #%u: offActual %#RX64, expected %#RX64", i, offActual, aSeeks[i].offActual);
3670 if (RT_SUCCESS(rc))
3671 {
3672 uint64_t offTell = RTFileTell(hFile1);
3673 if (offTell != offActual)
3674 RTTestIFailed("Seek #%u: offActual %#RX64, RTFileTell %#RX64", i, offActual, offTell);
3675 }
3676
3677 if (RT_SUCCESS(rc) && offActual + _2K <= cbFile && iDoReadCheck)
3678 {
3679 uint8_t abBuf[_2K];
3680 RTTESTI_CHECK_RC(rc = RTFileRead(hFile1, abBuf, sizeof(abBuf), NULL), VINF_SUCCESS);
3681 if (RT_SUCCESS(rc))
3682 {
3683 size_t offMarker = (size_t)(RT_ALIGN_64(offActual, _1K) - offActual);
3684 uint64_t uMarker = *(uint64_t *)&abBuf[offMarker]; /** @todo potentially unaligned access */
3685 if (uMarker != offActual + offMarker)
3686 RTTestIFailed("Seek #%u: Invalid marker value (@ %#RX64): %#RX64, expected %#RX64",
3687 i, offActual, uMarker, offActual + offMarker);
3688
3689 RTTESTI_CHECK_RC(RTFileSeek(hFile1, -(int64_t)sizeof(abBuf), RTFILE_SEEK_CURRENT, NULL), VINF_SUCCESS);
3690 }
3691 }
3692 }
3693 }
3694
3695
3696 /*
3697 * Profile seeking relative to the beginning of the file and relative
3698 * to the end. The latter might be more expensive in a SF context.
3699 */
3700 PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? iIteration : iIteration % cbFile, RTFILE_SEEK_BEGIN, NULL),
3701 g_nsTestRun, "RTFileSeek/BEGIN");
3702 PROFILE_FN(RTFileSeek(hFile1, iIteration < cbFile ? -(int64_t)iIteration : -(int64_t)(iIteration % cbFile), RTFILE_SEEK_END, NULL),
3703 g_nsTestRun, "RTFileSeek/END");
3704
3705}
3706
3707#ifdef FSPERF_TEST_SENDFILE
3708
3709/**
3710 * Send file thread arguments.
3711 */
3712typedef struct FSPERFSENDFILEARGS
3713{
3714 uint64_t offFile;
3715 size_t cbSend;
3716 uint64_t cbSent;
3717 size_t cbBuf;
3718 uint8_t *pbBuf;
3719 uint8_t bFiller;
3720 bool fCheckBuf;
3721 RTSOCKET hSocket;
3722 uint64_t volatile tsThreadDone;
3723} FSPERFSENDFILEARGS;
3724
3725/** Thread receiving the bytes from a sendfile() call. */
3726static DECLCALLBACK(int) fsPerfSendFileThread(RTTHREAD hSelf, void *pvUser)
3727{
3728 FSPERFSENDFILEARGS *pArgs = (FSPERFSENDFILEARGS *)pvUser;
3729 int rc = VINF_SUCCESS;
3730
3731 if (pArgs->fCheckBuf)
3732 RTTestSetDefault(g_hTest, NULL);
3733
3734 uint64_t cbReceived = 0;
3735 while (cbReceived < pArgs->cbSent)
3736 {
3737 size_t const cbToRead = RT_MIN(pArgs->cbBuf, pArgs->cbSent - cbReceived);
3738 size_t cbActual = 0;
3739 RTTEST_CHECK_RC_BREAK(g_hTest, rc = RTTcpRead(pArgs->hSocket, pArgs->pbBuf, cbToRead, &cbActual), VINF_SUCCESS);
3740 RTTEST_CHECK_BREAK(g_hTest, cbActual != 0);
3741 RTTEST_CHECK(g_hTest, cbActual <= cbToRead);
3742 if (pArgs->fCheckBuf)
3743 fsPerfCheckReadBuf(__LINE__, pArgs->offFile + cbReceived, pArgs->pbBuf, cbActual, pArgs->bFiller);
3744 cbReceived += cbActual;
3745 }
3746
3747 pArgs->tsThreadDone = RTTimeNanoTS();
3748
3749 if (cbReceived == pArgs->cbSent && RT_SUCCESS(rc))
3750 {
3751 size_t cbActual = 0;
3752 rc = RTSocketReadNB(pArgs->hSocket, pArgs->pbBuf, 1, &cbActual);
3753 if (rc != VINF_SUCCESS && rc != VINF_TRY_AGAIN)
3754 RTTestFailed(g_hTest, "RTSocketReadNB(sendfile client socket) -> %Rrc; expected VINF_SUCCESS or VINF_TRY_AGAIN\n", rc);
3755 else if (cbActual != 0)
3756 RTTestFailed(g_hTest, "sendfile client socket still contains data when done!\n");
3757 }
3758
3759 RTTEST_CHECK_RC(g_hTest, RTSocketClose(pArgs->hSocket), VINF_SUCCESS);
3760 pArgs->hSocket = NIL_RTSOCKET;
3761
3762 RT_NOREF(hSelf);
3763 return rc;
3764}
3765
3766
3767static uint64_t fsPerfSendFileOne(FSPERFSENDFILEARGS *pArgs, RTFILE hFile1, uint64_t offFile,
3768 size_t cbSend, uint64_t cbSent, uint8_t bFiller, bool fCheckBuf, unsigned iLine)
3769{
3770 /* Copy parameters to the argument structure: */
3771 pArgs->offFile = offFile;
3772 pArgs->cbSend = cbSend;
3773 pArgs->cbSent = cbSent;
3774 pArgs->bFiller = bFiller;
3775 pArgs->fCheckBuf = fCheckBuf;
3776
3777 /* Create a socket pair. */
3778 pArgs->hSocket = NIL_RTSOCKET;
3779 RTSOCKET hServer = NIL_RTSOCKET;
3780 RTTESTI_CHECK_RC_RET(RTTcpCreatePair(&hServer, &pArgs->hSocket, 0), VINF_SUCCESS, 0);
3781
3782 /* Create the receiving thread: */
3783 int rc;
3784 RTTHREAD hThread = NIL_RTTHREAD;
3785 RTTESTI_CHECK_RC(rc = RTThreadCreate(&hThread, fsPerfSendFileThread, pArgs, 0,
3786 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "sendfile"), VINF_SUCCESS);
3787 if (RT_SUCCESS(rc))
3788 {
3789 uint64_t const tsStart = RTTimeNanoTS();
3790
3791# if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
3792 /* SystemV sendfile: */
3793 loff_t offFileSf = pArgs->offFile;
3794 ssize_t cbActual = sendfile((int)RTSocketToNative(hServer), (int)RTFileToNative(hFile1), &offFileSf, pArgs->cbSend);
3795 int const iErr = errno;
3796 if (cbActual < 0)
3797 RTTestIFailed("%u: sendfile(socket, file, &%#X64, %#zx) failed (%zd): %d (%Rrc), offFileSf=%#RX64\n",
3798 iLine, pArgs->offFile, pArgs->cbSend, cbActual, iErr, RTErrConvertFromErrno(iErr), (uint64_t)offFileSf);
3799 else if ((uint64_t)cbActual != pArgs->cbSent)
3800 RTTestIFailed("%u: sendfile(socket, file, &%#RX64, %#zx): %#zx, expected %#RX64 (offFileSf=%#RX64)\n",
3801 iLine, pArgs->offFile, pArgs->cbSend, cbActual, pArgs->cbSent, (uint64_t)offFileSf);
3802 else if ((uint64_t)offFileSf != pArgs->offFile + pArgs->cbSent)
3803 RTTestIFailed("%u: sendfile(socket, file, &%#RX64, %#zx): %#zx; offFileSf=%#RX64, expected %#RX64\n",
3804 iLine, pArgs->offFile, pArgs->cbSend, cbActual, (uint64_t)offFileSf, pArgs->offFile + pArgs->cbSent);
3805#else
3806 /* BSD sendfile: */
3807# ifdef SF_SYNC
3808 int fSfFlags = SF_SYNC;
3809# else
3810 int fSfFlags = 0;
3811# endif
3812 off_t cbActual = pArgs->cbSend;
3813 rc = sendfile((int)RTFileToNative(hFile1), (int)RTSocketToNative(hServer),
3814# ifdef RT_OS_DARWIN
3815 pArgs->offFile, &cbActual, NULL, fSfFlags);
3816# else
3817 pArgs->offFile, cbActual, NULL, &cbActual, fSfFlags);
3818# endif
3819 int const iErr = errno;
3820 if (rc != 0)
3821 RTTestIFailed("%u: sendfile(file, socket, %#RX64, %#zx, NULL,, %#x) failed (%d): %d (%Rrc), cbActual=%#RX64\n",
3822 iLine, pArgs->offFile, (size_t)pArgs->cbSend, rc, iErr, RTErrConvertFromErrno(iErr), (uint64_t)cbActual);
3823 if ((uint64_t)cbActual != pArgs->cbSent)
3824 RTTestIFailed("%u: sendfile(file, socket, %#RX64, %#zx, NULL,, %#x): cbActual=%#RX64, expected %#RX64 (rc=%d, errno=%d)\n",
3825 iLine, pArgs->offFile, (size_t)pArgs->cbSend, (uint64_t)cbActual, pArgs->cbSent, rc, iErr);
3826# endif
3827 RTTESTI_CHECK_RC(RTSocketClose(hServer), VINF_SUCCESS);
3828 RTTESTI_CHECK_RC(RTThreadWait(hThread, 30 * RT_NS_1SEC, NULL), VINF_SUCCESS);
3829
3830 if (pArgs->tsThreadDone >= tsStart)
3831 return RT_MAX(pArgs->tsThreadDone - tsStart, 1);
3832 }
3833 return 0;
3834}
3835
3836
3837static void fsPerfSendFile(RTFILE hFile1, uint64_t cbFile)
3838{
3839 RTTestISub("sendfile");
3840# ifdef RT_OS_LINUX
3841 uint64_t const cbFileMax = RT_MIN(cbFile, UINT32_MAX - PAGE_OFFSET_MASK);
3842# else
3843 uint64_t const cbFileMax = RT_MIN(cbFile, SSIZE_MAX - PAGE_OFFSET_MASK);
3844# endif
3845 signal(SIGPIPE, SIG_IGN);
3846
3847 /*
3848 * Allocate a buffer.
3849 */
3850 FSPERFSENDFILEARGS Args;
3851 Args.cbBuf = RT_MIN(cbFileMax, _16M);
3852 Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
3853 while (!Args.pbBuf)
3854 {
3855 Args.cbBuf /= 8;
3856 RTTESTI_CHECK_RETV(Args.cbBuf >= _64K);
3857 Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
3858 }
3859
3860 /*
3861 * First iteration with default buffer content.
3862 */
3863 fsPerfSendFileOne(&Args, hFile1, 0, cbFileMax, cbFileMax, 0xf6, true /*fCheckBuf*/, __LINE__);
3864 if (cbFileMax == cbFile)
3865 fsPerfSendFileOne(&Args, hFile1, 63, cbFileMax, cbFileMax - 63, 0xf6, true /*fCheckBuf*/, __LINE__);
3866 else
3867 fsPerfSendFileOne(&Args, hFile1, 63, cbFileMax - 63, cbFileMax - 63, 0xf6, true /*fCheckBuf*/, __LINE__);
3868
3869 /*
3870 * Write a block using the regular API and then send it, checking that
3871 * the any caching that sendfile does is correctly updated.
3872 */
3873 uint8_t bFiller = 0xf6;
3874 size_t cbToSend = RT_MIN(cbFileMax, Args.cbBuf);
3875 do
3876 {
3877 fsPerfSendFileOne(&Args, hFile1, 0, cbToSend, cbToSend, bFiller, true /*fCheckBuf*/, __LINE__); /* prime cache */
3878
3879 bFiller += 1;
3880 fsPerfFillWriteBuf(0, Args.pbBuf, cbToSend, bFiller);
3881 RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, 0, Args.pbBuf, cbToSend, NULL), VINF_SUCCESS);
3882
3883 fsPerfSendFileOne(&Args, hFile1, 0, cbToSend, cbToSend, bFiller, true /*fCheckBuf*/, __LINE__);
3884
3885 cbToSend /= 2;
3886 } while (cbToSend >= PAGE_SIZE && ((unsigned)bFiller - 0xf7U) < 64);
3887
3888 /*
3889 * Restore buffer content
3890 */
3891 bFiller = 0xf6;
3892 fsPerfFillWriteBuf(0, Args.pbBuf, Args.cbBuf, bFiller);
3893 RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, 0, Args.pbBuf, Args.cbBuf, NULL), VINF_SUCCESS);
3894
3895 /*
3896 * Do 128 random sends.
3897 */
3898 uint64_t const cbSmall = RT_MIN(_256K, cbFileMax / 16);
3899 for (uint32_t iTest = 0; iTest < 128; iTest++)
3900 {
3901 cbToSend = (size_t)RTRandU64Ex(1, iTest < 64 ? cbSmall : cbFileMax);
3902 uint64_t const offToSendFrom = RTRandU64Ex(0, cbFile - 1);
3903 uint64_t const cbSent = offToSendFrom + cbToSend <= cbFile ? cbToSend : cbFile - offToSendFrom;
3904
3905 fsPerfSendFileOne(&Args, hFile1, offToSendFrom, cbToSend, cbSent, bFiller, true /*fCheckBuf*/, __LINE__);
3906 }
3907
3908 /*
3909 * Benchmark it.
3910 */
3911 uint32_t cIterations = 0;
3912 uint64_t nsElapsed = 0;
3913 for (;;)
3914 {
3915 uint64_t cNsThis = fsPerfSendFileOne(&Args, hFile1, 0, cbFileMax, cbFileMax, 0xf6, false /*fCheckBuf*/, __LINE__);
3916 nsElapsed += cNsThis;
3917 cIterations++;
3918 if (!cNsThis || nsElapsed >= g_nsTestRun)
3919 break;
3920 }
3921 uint64_t cbTotal = cbFileMax * cIterations;
3922 RTTestIValue("latency", nsElapsed / cIterations, RTTESTUNIT_NS_PER_CALL);
3923 RTTestIValue("throughput", (uint64_t)(cbTotal / ((double)nsElapsed / RT_NS_1SEC)), RTTESTUNIT_BYTES_PER_SEC);
3924 RTTestIValue("calls", cIterations, RTTESTUNIT_CALLS);
3925 RTTestIValue("bytes", cbTotal, RTTESTUNIT_BYTES);
3926 if (g_fShowDuration)
3927 RTTestIValue("duration", nsElapsed, RTTESTUNIT_NS);
3928
3929 /*
3930 * Cleanup.
3931 */
3932 RTMemFree(Args.pbBuf);
3933}
3934
3935#endif /* FSPERF_TEST_SENDFILE */
3936#ifdef RT_OS_LINUX
3937
3938#ifndef __NR_splice
3939# if defined(RT_ARCH_AMD64)
3940# define __NR_splice 275
3941# elif defined(RT_ARCH_X86)
3942# define __NR_splice 313
3943# else
3944# error "fix me"
3945# endif
3946#endif
3947
3948/** FsPerf is built against ancient glibc, so make the splice syscall ourselves. */
3949DECLINLINE(ssize_t) syscall_splice(int fdIn, loff_t *poffIn, int fdOut, loff_t *poffOut, size_t cbChunk, unsigned fFlags)
3950{
3951 return syscall(__NR_splice, fdIn, poffIn, fdOut, poffOut, cbChunk, fFlags);
3952}
3953
3954
3955/**
3956 * Send file thread arguments.
3957 */
3958typedef struct FSPERFSPLICEARGS
3959{
3960 uint64_t offFile;
3961 size_t cbSend;
3962 uint64_t cbSent;
3963 size_t cbBuf;
3964 uint8_t *pbBuf;
3965 uint8_t bFiller;
3966 bool fCheckBuf;
3967 uint32_t cCalls;
3968 RTPIPE hPipe;
3969 uint64_t volatile tsThreadDone;
3970} FSPERFSPLICEARGS;
3971
3972
3973/** Thread receiving the bytes from a splice() call. */
3974static DECLCALLBACK(int) fsPerfSpliceToPipeThread(RTTHREAD hSelf, void *pvUser)
3975{
3976 FSPERFSPLICEARGS *pArgs = (FSPERFSPLICEARGS *)pvUser;
3977 int rc = VINF_SUCCESS;
3978
3979 if (pArgs->fCheckBuf)
3980 RTTestSetDefault(g_hTest, NULL);
3981
3982 uint64_t cbReceived = 0;
3983 while (cbReceived < pArgs->cbSent)
3984 {
3985 size_t const cbToRead = RT_MIN(pArgs->cbBuf, pArgs->cbSent - cbReceived);
3986 size_t cbActual = 0;
3987 RTTEST_CHECK_RC_BREAK(g_hTest, rc = RTPipeReadBlocking(pArgs->hPipe, pArgs->pbBuf, cbToRead, &cbActual), VINF_SUCCESS);
3988 RTTEST_CHECK_BREAK(g_hTest, cbActual != 0);
3989 RTTEST_CHECK(g_hTest, cbActual <= cbToRead);
3990 if (pArgs->fCheckBuf)
3991 fsPerfCheckReadBuf(__LINE__, pArgs->offFile + cbReceived, pArgs->pbBuf, cbActual, pArgs->bFiller);
3992 cbReceived += cbActual;
3993 }
3994
3995 pArgs->tsThreadDone = RTTimeNanoTS();
3996
3997 if (cbReceived == pArgs->cbSent && RT_SUCCESS(rc))
3998 {
3999 size_t cbActual = 0;
4000 rc = RTPipeRead(pArgs->hPipe, pArgs->pbBuf, 1, &cbActual);
4001 if (rc != VINF_SUCCESS && rc != VINF_TRY_AGAIN && rc != VERR_BROKEN_PIPE)
4002 RTTestFailed(g_hTest, "RTPipeReadBlocking() -> %Rrc; expected VINF_SUCCESS or VINF_TRY_AGAIN\n", rc);
4003 else if (cbActual != 0)
4004 RTTestFailed(g_hTest, "splice read pipe still contains data when done!\n");
4005 }
4006
4007 RTTEST_CHECK_RC(g_hTest, RTPipeClose(pArgs->hPipe), VINF_SUCCESS);
4008 pArgs->hPipe = NIL_RTPIPE;
4009
4010 RT_NOREF(hSelf);
4011 return rc;
4012}
4013
4014
4015/** Sends hFile1 to a pipe via the Linux-specific splice() syscall. */
4016static uint64_t fsPerfSpliceToPipeOne(FSPERFSPLICEARGS *pArgs, RTFILE hFile1, uint64_t offFile,
4017 size_t cbSend, uint64_t cbSent, uint8_t bFiller, bool fCheckBuf, unsigned iLine)
4018{
4019 /* Copy parameters to the argument structure: */
4020 pArgs->offFile = offFile;
4021 pArgs->cbSend = cbSend;
4022 pArgs->cbSent = cbSent;
4023 pArgs->bFiller = bFiller;
4024 pArgs->fCheckBuf = fCheckBuf;
4025
4026 /* Create a socket pair. */
4027 pArgs->hPipe = NIL_RTPIPE;
4028 RTPIPE hPipeW = NIL_RTPIPE;
4029 RTTESTI_CHECK_RC_RET(RTPipeCreate(&pArgs->hPipe, &hPipeW, 0 /*fFlags*/), VINF_SUCCESS, 0);
4030
4031 /* Create the receiving thread: */
4032 int rc;
4033 RTTHREAD hThread = NIL_RTTHREAD;
4034 RTTESTI_CHECK_RC(rc = RTThreadCreate(&hThread, fsPerfSpliceToPipeThread, pArgs, 0,
4035 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "splicerecv"), VINF_SUCCESS);
4036 if (RT_SUCCESS(rc))
4037 {
4038 uint64_t const tsStart = RTTimeNanoTS();
4039 size_t cbLeft = cbSend;
4040 size_t cbTotal = 0;
4041 do
4042 {
4043 loff_t offFileIn = offFile;
4044 ssize_t cbActual = syscall_splice((int)RTFileToNative(hFile1), &offFileIn, (int)RTPipeToNative(hPipeW), NULL,
4045 cbLeft, 0 /*fFlags*/);
4046 int const iErr = errno;
4047 if (RT_UNLIKELY(cbActual < 0))
4048 {
4049 if (iErr == EPIPE && cbTotal == pArgs->cbSent)
4050 break;
4051 RTTestIFailed("%u: splice(file, &%#RX64, pipe, NULL, %#zx, 0) failed (%zd): %d (%Rrc), offFileIn=%#RX64\n",
4052 iLine, offFile, cbLeft, cbActual, iErr, RTErrConvertFromErrno(iErr), (uint64_t)offFileIn);
4053 break;
4054 }
4055 RTTESTI_CHECK_BREAK((uint64_t)cbActual <= cbLeft);
4056 if ((uint64_t)offFileIn != offFile + (uint64_t)cbActual)
4057 {
4058 RTTestIFailed("%u: splice(file, &%#RX64, pipe, NULL, %#zx, 0): %#zx; offFileIn=%#RX64, expected %#RX64\n",
4059 iLine, offFile, cbLeft, cbActual, (uint64_t)offFileIn, offFile + (uint64_t)cbActual);
4060 break;
4061 }
4062 if (cbActual > 0)
4063 {
4064 pArgs->cCalls++;
4065 offFile += (size_t)cbActual;
4066 cbTotal += (size_t)cbActual;
4067 cbLeft -= (size_t)cbActual;
4068 }
4069 else
4070 break;
4071 } while (cbLeft > 0);
4072
4073 if (cbTotal != pArgs->cbSent)
4074 RTTestIFailed("%u: spliced a total of %#zx bytes, expected %#zx!\n", iLine, cbTotal, pArgs->cbSent);
4075
4076 RTTESTI_CHECK_RC(RTPipeClose(hPipeW), VINF_SUCCESS);
4077 RTTESTI_CHECK_RC(RTThreadWait(hThread, 30 * RT_NS_1SEC, NULL), VINF_SUCCESS);
4078
4079 if (pArgs->tsThreadDone >= tsStart)
4080 return RT_MAX(pArgs->tsThreadDone - tsStart, 1);
4081 }
4082 return 0;
4083}
4084
4085
4086static void fsPerfSpliceToPipe(RTFILE hFile1, uint64_t cbFile)
4087{
4088 RTTestISub("splice/to-pipe");
4089
4090 /*
4091 * splice was introduced in 2.6.17 according to the man-page.
4092 */
4093 char szRelease[64];
4094 RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szRelease, sizeof(szRelease));
4095 if (RTStrVersionCompare(szRelease, "2.6.17") < 0)
4096 {
4097 RTTestPassed(g_hTest, "too old kernel (%s)", szRelease);
4098 return;
4099 }
4100
4101 uint64_t const cbFileMax = RT_MIN(cbFile, UINT32_MAX - PAGE_OFFSET_MASK);
4102 signal(SIGPIPE, SIG_IGN);
4103
4104 /*
4105 * Allocate a buffer.
4106 */
4107 FSPERFSPLICEARGS Args;
4108 Args.cbBuf = RT_MIN(cbFileMax, _16M);
4109 Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
4110 while (!Args.pbBuf)
4111 {
4112 Args.cbBuf /= 8;
4113 RTTESTI_CHECK_RETV(Args.cbBuf >= _64K);
4114 Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
4115 }
4116
4117 /*
4118 * First iteration with default buffer content.
4119 */
4120 fsPerfSpliceToPipeOne(&Args, hFile1, 0, cbFileMax, cbFileMax, 0xf6, true /*fCheckBuf*/, __LINE__);
4121 if (cbFileMax == cbFile)
4122 fsPerfSpliceToPipeOne(&Args, hFile1, 63, cbFileMax, cbFileMax - 63, 0xf6, true /*fCheckBuf*/, __LINE__);
4123 else
4124 fsPerfSpliceToPipeOne(&Args, hFile1, 63, cbFileMax - 63, cbFileMax - 63, 0xf6, true /*fCheckBuf*/, __LINE__);
4125
4126 /*
4127 * Write a block using the regular API and then send it, checking that
4128 * the any caching that sendfile does is correctly updated.
4129 */
4130 uint8_t bFiller = 0xf6;
4131 size_t cbToSend = RT_MIN(cbFileMax, Args.cbBuf);
4132 do
4133 {
4134 fsPerfSpliceToPipeOne(&Args, hFile1, 0, cbToSend, cbToSend, bFiller, true /*fCheckBuf*/, __LINE__); /* prime cache */
4135
4136 bFiller += 1;
4137 fsPerfFillWriteBuf(0, Args.pbBuf, cbToSend, bFiller);
4138 RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, 0, Args.pbBuf, cbToSend, NULL), VINF_SUCCESS);
4139
4140 fsPerfSpliceToPipeOne(&Args, hFile1, 0, cbToSend, cbToSend, bFiller, true /*fCheckBuf*/, __LINE__);
4141
4142 cbToSend /= 2;
4143 } while (cbToSend >= PAGE_SIZE && ((unsigned)bFiller - 0xf7U) < 64);
4144
4145 /*
4146 * Restore buffer content
4147 */
4148 bFiller = 0xf6;
4149 fsPerfFillWriteBuf(0, Args.pbBuf, Args.cbBuf, bFiller);
4150 RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, 0, Args.pbBuf, Args.cbBuf, NULL), VINF_SUCCESS);
4151
4152 /*
4153 * Do 128 random sends.
4154 */
4155 uint64_t const cbSmall = RT_MIN(_256K, cbFileMax / 16);
4156 for (uint32_t iTest = 0; iTest < 128; iTest++)
4157 {
4158 cbToSend = (size_t)RTRandU64Ex(1, iTest < 64 ? cbSmall : cbFileMax);
4159 uint64_t const offToSendFrom = RTRandU64Ex(0, cbFile - 1);
4160 uint64_t const cbSent = offToSendFrom + cbToSend <= cbFile ? cbToSend : cbFile - offToSendFrom;
4161
4162 fsPerfSpliceToPipeOne(&Args, hFile1, offToSendFrom, cbToSend, cbSent, bFiller, true /*fCheckBuf*/, __LINE__);
4163 }
4164
4165 /*
4166 * Benchmark it.
4167 */
4168 Args.cCalls = 0;
4169 uint32_t cIterations = 0;
4170 uint64_t nsElapsed = 0;
4171 for (;;)
4172 {
4173 uint64_t cNsThis = fsPerfSpliceToPipeOne(&Args, hFile1, 0, cbFileMax, cbFileMax, 0xf6, false /*fCheckBuf*/, __LINE__);
4174 nsElapsed += cNsThis;
4175 cIterations++;
4176 if (!cNsThis || nsElapsed >= g_nsTestRun)
4177 break;
4178 }
4179 uint64_t cbTotal = cbFileMax * cIterations;
4180 RTTestIValue("latency", nsElapsed / Args.cCalls, RTTESTUNIT_NS_PER_CALL);
4181 RTTestIValue("throughput", (uint64_t)(cbTotal / ((double)nsElapsed / RT_NS_1SEC)), RTTESTUNIT_BYTES_PER_SEC);
4182 RTTestIValue("calls", Args.cCalls, RTTESTUNIT_CALLS);
4183 RTTestIValue("bytes/call", cbTotal / Args.cCalls, RTTESTUNIT_BYTES);
4184 RTTestIValue("iterations", cIterations, RTTESTUNIT_NONE);
4185 RTTestIValue("bytes", cbTotal, RTTESTUNIT_BYTES);
4186 if (g_fShowDuration)
4187 RTTestIValue("duration", nsElapsed, RTTESTUNIT_NS);
4188
4189 /*
4190 * Cleanup.
4191 */
4192 RTMemFree(Args.pbBuf);
4193}
4194
4195
4196/** Thread sending the bytes to a splice() call. */
4197static DECLCALLBACK(int) fsPerfSpliceToFileThread(RTTHREAD hSelf, void *pvUser)
4198{
4199 FSPERFSPLICEARGS *pArgs = (FSPERFSPLICEARGS *)pvUser;
4200 int rc = VINF_SUCCESS;
4201
4202 uint64_t offFile = pArgs->offFile;
4203 uint64_t cbTotalSent = 0;
4204 while (cbTotalSent < pArgs->cbSent)
4205 {
4206 size_t const cbToSend = RT_MIN(pArgs->cbBuf, pArgs->cbSent - cbTotalSent);
4207 fsPerfFillWriteBuf(offFile, pArgs->pbBuf, cbToSend, pArgs->bFiller);
4208 RTTEST_CHECK_RC_BREAK(g_hTest, rc = RTPipeWriteBlocking(pArgs->hPipe, pArgs->pbBuf, cbToSend, NULL), VINF_SUCCESS);
4209 offFile += cbToSend;
4210 cbTotalSent += cbToSend;
4211 }
4212
4213 pArgs->tsThreadDone = RTTimeNanoTS();
4214
4215 RTTEST_CHECK_RC(g_hTest, RTPipeClose(pArgs->hPipe), VINF_SUCCESS);
4216 pArgs->hPipe = NIL_RTPIPE;
4217
4218 RT_NOREF(hSelf);
4219 return rc;
4220}
4221
4222
4223/** Fill hFile1 via a pipe and the Linux-specific splice() syscall. */
4224static uint64_t fsPerfSpliceToFileOne(FSPERFSPLICEARGS *pArgs, RTFILE hFile1, uint64_t offFile,
4225 size_t cbSend, uint64_t cbSent, uint8_t bFiller, bool fCheckFile, unsigned iLine)
4226{
4227 /* Copy parameters to the argument structure: */
4228 pArgs->offFile = offFile;
4229 pArgs->cbSend = cbSend;
4230 pArgs->cbSent = cbSent;
4231 pArgs->bFiller = bFiller;
4232 pArgs->fCheckBuf = false;
4233
4234 /* Create a socket pair. */
4235 pArgs->hPipe = NIL_RTPIPE;
4236 RTPIPE hPipeR = NIL_RTPIPE;
4237 RTTESTI_CHECK_RC_RET(RTPipeCreate(&hPipeR, &pArgs->hPipe, 0 /*fFlags*/), VINF_SUCCESS, 0);
4238
4239 /* Create the receiving thread: */
4240 int rc;
4241 RTTHREAD hThread = NIL_RTTHREAD;
4242 RTTESTI_CHECK_RC(rc = RTThreadCreate(&hThread, fsPerfSpliceToFileThread, pArgs, 0,
4243 RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "splicerecv"), VINF_SUCCESS);
4244 if (RT_SUCCESS(rc))
4245 {
4246 /*
4247 * Do the splicing.
4248 */
4249 uint64_t const tsStart = RTTimeNanoTS();
4250 size_t cbLeft = cbSend;
4251 size_t cbTotal = 0;
4252 do
4253 {
4254 loff_t offFileOut = offFile;
4255 ssize_t cbActual = syscall_splice((int)RTPipeToNative(hPipeR), NULL, (int)RTFileToNative(hFile1), &offFileOut,
4256 cbLeft, 0 /*fFlags*/);
4257 int const iErr = errno;
4258 if (RT_UNLIKELY(cbActual < 0))
4259 {
4260 RTTestIFailed("%u: splice(pipe, NULL, file, &%#RX64, %#zx, 0) failed (%zd): %d (%Rrc), offFileOut=%#RX64\n",
4261 iLine, offFile, cbLeft, cbActual, iErr, RTErrConvertFromErrno(iErr), (uint64_t)offFileOut);
4262 break;
4263 }
4264 RTTESTI_CHECK_BREAK((uint64_t)cbActual <= cbLeft);
4265 if ((uint64_t)offFileOut != offFile + (uint64_t)cbActual)
4266 {
4267 RTTestIFailed("%u: splice(pipe, NULL, file, &%#RX64, %#zx, 0): %#zx; offFileOut=%#RX64, expected %#RX64\n",
4268 iLine, offFile, cbLeft, cbActual, (uint64_t)offFileOut, offFile + (uint64_t)cbActual);
4269 break;
4270 }
4271 if (cbActual > 0)
4272 {
4273 pArgs->cCalls++;
4274 offFile += (size_t)cbActual;
4275 cbTotal += (size_t)cbActual;
4276 cbLeft -= (size_t)cbActual;
4277 }
4278 else
4279 break;
4280 } while (cbLeft > 0);
4281 uint64_t const nsElapsed = RTTimeNanoTS() - tsStart;
4282
4283 if (cbTotal != pArgs->cbSent)
4284 RTTestIFailed("%u: spliced a total of %#zx bytes, expected %#zx!\n", iLine, cbTotal, pArgs->cbSent);
4285
4286 RTTESTI_CHECK_RC(RTPipeClose(hPipeR), VINF_SUCCESS);
4287 RTTESTI_CHECK_RC(RTThreadWait(hThread, 30 * RT_NS_1SEC, NULL), VINF_SUCCESS);
4288
4289 /* Check the file content. */
4290 if (fCheckFile && cbTotal == pArgs->cbSent)
4291 {
4292 offFile = pArgs->offFile;
4293 cbLeft = cbSent;
4294 while (cbLeft > 0)
4295 {
4296 size_t cbToRead = RT_MIN(cbLeft, pArgs->cbBuf);
4297 RTTESTI_CHECK_RC_BREAK(RTFileReadAt(hFile1, offFile, pArgs->pbBuf, cbToRead, NULL), VINF_SUCCESS);
4298 if (!fsPerfCheckReadBuf(iLine, offFile, pArgs->pbBuf, cbToRead, pArgs->bFiller))
4299 break;
4300 offFile += cbToRead;
4301 cbLeft -= cbToRead;
4302 }
4303 }
4304 return nsElapsed;
4305 }
4306 return 0;
4307}
4308
4309
4310static void fsPerfSpliceToFile(RTFILE hFile1, uint64_t cbFile)
4311{
4312 RTTestISub("splice/to-file");
4313
4314 /*
4315 * splice was introduced in 2.6.17 according to the man-page.
4316 */
4317 char szRelease[64];
4318 RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szRelease, sizeof(szRelease));
4319 if (RTStrVersionCompare(szRelease, "2.6.17") < 0)
4320 {
4321 RTTestPassed(g_hTest, "too old kernel (%s)", szRelease);
4322 return;
4323 }
4324
4325 uint64_t const cbFileMax = RT_MIN(cbFile, UINT32_MAX - PAGE_OFFSET_MASK);
4326 signal(SIGPIPE, SIG_IGN);
4327
4328 /*
4329 * Allocate a buffer.
4330 */
4331 FSPERFSPLICEARGS Args;
4332 Args.cbBuf = RT_MIN(cbFileMax, _16M);
4333 Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
4334 while (!Args.pbBuf)
4335 {
4336 Args.cbBuf /= 8;
4337 RTTESTI_CHECK_RETV(Args.cbBuf >= _64K);
4338 Args.pbBuf = (uint8_t *)RTMemAlloc(Args.cbBuf);
4339 }
4340
4341 /*
4342 * Do the whole file.
4343 */
4344 uint8_t bFiller = 0x76;
4345 fsPerfSpliceToFileOne(&Args, hFile1, 0, cbFileMax, cbFileMax, bFiller, true /*fCheckFile*/, __LINE__);
4346
4347 /*
4348 * Do 64 random chunks (this is slower).
4349 */
4350 uint64_t const cbSmall = RT_MIN(_256K, cbFileMax / 16);
4351 for (uint32_t iTest = 0; iTest < 64; iTest++)
4352 {
4353 size_t const cbToWrite = (size_t)RTRandU64Ex(1, iTest < 24 ? cbSmall : cbFileMax);
4354 uint64_t const offToWriteAt = RTRandU64Ex(0, cbFile - cbToWrite);
4355 uint64_t const cbTryRead = cbToWrite + (iTest & 1 ? RTRandU32Ex(0, _64K) : 0);
4356
4357 bFiller++;
4358 fsPerfSpliceToFileOne(&Args, hFile1, offToWriteAt, cbTryRead, cbToWrite, bFiller, true /*fCheckFile*/, __LINE__);
4359 }
4360
4361 /*
4362 * Benchmark it.
4363 */
4364 Args.cCalls = 0;
4365 uint32_t cIterations = 0;
4366 uint64_t nsElapsed = 0;
4367 for (;;)
4368 {
4369 uint64_t cNsThis = fsPerfSpliceToFileOne(&Args, hFile1, 0, cbFileMax, cbFileMax, 0xf6, false /*fCheckBuf*/, __LINE__);
4370 nsElapsed += cNsThis;
4371 cIterations++;
4372 if (!cNsThis || nsElapsed >= g_nsTestRun)
4373 break;
4374 }
4375 uint64_t cbTotal = cbFileMax * cIterations;
4376 RTTestIValue("latency", nsElapsed / Args.cCalls, RTTESTUNIT_NS_PER_CALL);
4377 RTTestIValue("throughput", (uint64_t)(cbTotal / ((double)nsElapsed / RT_NS_1SEC)), RTTESTUNIT_BYTES_PER_SEC);
4378 RTTestIValue("calls", Args.cCalls, RTTESTUNIT_CALLS);
4379 RTTestIValue("bytes/call", cbTotal / Args.cCalls, RTTESTUNIT_BYTES);
4380 RTTestIValue("iterations", cIterations, RTTESTUNIT_NONE);
4381 RTTestIValue("bytes", cbTotal, RTTESTUNIT_BYTES);
4382 if (g_fShowDuration)
4383 RTTestIValue("duration", nsElapsed, RTTESTUNIT_NS);
4384
4385 /*
4386 * Cleanup.
4387 */
4388 RTMemFree(Args.pbBuf);
4389}
4390
4391#endif /* RT_OS_LINUX */
4392
4393/** For fsPerfIoRead and fsPerfIoWrite. */
4394#define PROFILE_IO_FN(a_szOperation, a_fnCall) \
4395 do \
4396 { \
4397 RTTESTI_CHECK_RC_RETV(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS); \
4398 uint64_t offActual = 0; \
4399 uint32_t cSeeks = 0; \
4400 \
4401 /* Estimate how many iterations we need to fill up the given timeslot: */ \
4402 fsPerfYield(); \
4403 uint64_t nsStart = RTTimeNanoTS(); \
4404 uint64_t ns; \
4405 do \
4406 ns = RTTimeNanoTS(); \
4407 while (ns == nsStart); \
4408 nsStart = ns; \
4409 \
4410 uint64_t iIteration = 0; \
4411 do \
4412 { \
4413 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
4414 iIteration++; \
4415 ns = RTTimeNanoTS() - nsStart; \
4416 } while (ns < RT_NS_10MS); \
4417 ns /= iIteration; \
4418 if (ns > g_nsPerNanoTSCall + 32) \
4419 ns -= g_nsPerNanoTSCall; \
4420 uint64_t cIterations = g_nsTestRun / ns; \
4421 if (cIterations < 2) \
4422 cIterations = 2; \
4423 else if (cIterations & 1) \
4424 cIterations++; \
4425 \
4426 /* Do the actual profiling: */ \
4427 cSeeks = 0; \
4428 iIteration = 0; \
4429 fsPerfYield(); \
4430 nsStart = RTTimeNanoTS(); \
4431 for (uint32_t iAdjust = 0; iAdjust < 4; iAdjust++) \
4432 { \
4433 for (; iIteration < cIterations; iIteration++)\
4434 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
4435 ns = RTTimeNanoTS() - nsStart;\
4436 if (ns >= g_nsTestRun - (g_nsTestRun / 10)) \
4437 break; \
4438 cIterations += cIterations / 4; \
4439 if (cIterations & 1) \
4440 cIterations++; \
4441 nsStart += g_nsPerNanoTSCall; \
4442 } \
4443 RTTestIValueF(ns / iIteration, \
4444 RTTESTUNIT_NS_PER_OCCURRENCE, a_szOperation "/seq/%RU32 latency", cbBlock); \
4445 RTTestIValueF((uint64_t)((uint64_t)iIteration * cbBlock / ((double)ns / RT_NS_1SEC)), \
4446 RTTESTUNIT_BYTES_PER_SEC, a_szOperation "/seq/%RU32 throughput", cbBlock); \
4447 RTTestIValueF(iIteration, \
4448 RTTESTUNIT_CALLS, a_szOperation "/seq/%RU32 calls", cbBlock); \
4449 RTTestIValueF((uint64_t)iIteration * cbBlock, \
4450 RTTESTUNIT_BYTES, a_szOperation "/seq/%RU32 bytes", cbBlock); \
4451 RTTestIValueF(cSeeks, \
4452 RTTESTUNIT_OCCURRENCES, a_szOperation "/seq/%RU32 seeks", cbBlock); \
4453 if (g_fShowDuration) \
4454 RTTestIValueF(ns, RTTESTUNIT_NS, a_szOperation "/seq/%RU32 duration", cbBlock); \
4455 } while (0)
4456
4457
4458/**
4459 * One RTFileRead profiling iteration.
4460 */
4461DECL_FORCE_INLINE(int) fsPerfIoReadWorker(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t *pbBlock,
4462 uint64_t *poffActual, uint32_t *pcSeeks)
4463{
4464 /* Do we need to seek back to the start? */
4465 if (*poffActual + cbBlock <= cbFile)
4466 { /* likely */ }
4467 else
4468 {
4469 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
4470 *pcSeeks += 1;
4471 *poffActual = 0;
4472 }
4473
4474 size_t cbActuallyRead = 0;
4475 RTTESTI_CHECK_RC_RET(RTFileRead(hFile1, pbBlock, cbBlock, &cbActuallyRead), VINF_SUCCESS, rcCheck);
4476 if (cbActuallyRead == cbBlock)
4477 {
4478 *poffActual += cbActuallyRead;
4479 return VINF_SUCCESS;
4480 }
4481 RTTestIFailed("RTFileRead at %#RX64 returned just %#x bytes, expected %#x", *poffActual, cbActuallyRead, cbBlock);
4482 *poffActual += cbActuallyRead;
4483 return VERR_READ_ERROR;
4484}
4485
4486
4487void fsPerfIoReadBlockSize(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock)
4488{
4489 RTTestISubF("IO - Sequential read %RU32", cbBlock);
4490 if (cbBlock <= cbFile)
4491 {
4492
4493 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBlock);
4494 if (pbBuf)
4495 {
4496 memset(pbBuf, 0xf7, cbBlock);
4497 PROFILE_IO_FN("RTFileRead", fsPerfIoReadWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks));
4498 RTMemPageFree(pbBuf, cbBlock);
4499 }
4500 else
4501 RTTestSkipped(g_hTest, "insufficient (virtual) memory available");
4502 }
4503 else
4504 RTTestSkipped(g_hTest, "test file too small");
4505}
4506
4507
4508/** preadv is too new to be useful, so we use the readv api via this wrapper. */
4509DECLINLINE(int) myFileSgReadAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToRead, size_t *pcbRead)
4510{
4511 int rc = RTFileSeek(hFile, off, RTFILE_SEEK_BEGIN, NULL);
4512 if (RT_SUCCESS(rc))
4513 rc = RTFileSgRead(hFile, pSgBuf, cbToRead, pcbRead);
4514 return rc;
4515}
4516
4517
4518void fsPerfRead(RTFILE hFile1, RTFILE hFileNoCache, uint64_t cbFile)
4519{
4520 RTTestISubF("IO - RTFileRead");
4521
4522 /*
4523 * Allocate a big buffer we can play around with. Min size is 1MB.
4524 */
4525 size_t cbBuf = cbFile < _64M ? (size_t)cbFile : _64M;
4526 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
4527 while (!pbBuf)
4528 {
4529 cbBuf /= 2;
4530 RTTESTI_CHECK_RETV(cbBuf >= _1M);
4531 pbBuf = (uint8_t *)RTMemPageAlloc(_32M);
4532 }
4533
4534#if 1
4535 /*
4536 * Start at the beginning and read the full buffer in random small chunks, thereby
4537 * checking that unaligned buffer addresses, size and file offsets work fine.
4538 */
4539 struct
4540 {
4541 uint64_t offFile;
4542 uint32_t cbMax;
4543 } aRuns[] = { { 0, 127 }, { cbFile - cbBuf, UINT32_MAX }, { 0, UINT32_MAX -1 }};
4544 for (uint32_t i = 0; i < RT_ELEMENTS(aRuns); i++)
4545 {
4546 memset(pbBuf, 0x55, cbBuf);
4547 RTTESTI_CHECK_RC(RTFileSeek(hFile1, aRuns[i].offFile, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
4548 for (size_t offBuf = 0; offBuf < cbBuf; )
4549 {
4550 uint32_t const cbLeft = (uint32_t)(cbBuf - offBuf);
4551 uint32_t const cbToRead = aRuns[i].cbMax < UINT32_MAX / 2 ? RTRandU32Ex(1, RT_MIN(aRuns[i].cbMax, cbLeft))
4552 : aRuns[i].cbMax == UINT32_MAX ? RTRandU32Ex(RT_MAX(cbLeft / 4, 1), cbLeft)
4553 : RTRandU32Ex(cbLeft >= _8K ? _8K : 1, RT_MIN(_1M, cbLeft));
4554 size_t cbActual = 0;
4555 RTTESTI_CHECK_RC(RTFileRead(hFile1, &pbBuf[offBuf], cbToRead, &cbActual), VINF_SUCCESS);
4556 if (cbActual == cbToRead)
4557 {
4558 offBuf += cbActual;
4559 RTTESTI_CHECK_MSG(RTFileTell(hFile1) == aRuns[i].offFile + offBuf,
4560 ("%#RX64, expected %#RX64\n", RTFileTell(hFile1), aRuns[i].offFile + offBuf));
4561 }
4562 else
4563 {
4564 RTTestIFailed("Attempting to read %#x bytes at %#zx, only got %#x bytes back! (cbLeft=%#x cbBuf=%#zx)\n",
4565 cbToRead, offBuf, cbActual, cbLeft, cbBuf);
4566 if (cbActual)
4567 offBuf += cbActual;
4568 else
4569 pbBuf[offBuf++] = 0x11;
4570 }
4571 }
4572 fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf);
4573 }
4574
4575 /*
4576 * Test reading beyond the end of the file.
4577 */
4578 size_t const acbMax[] = { cbBuf, _64K, _16K, _4K, 256 };
4579 uint32_t const aoffFromEos[] =
4580 { 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,
4581 4092, 4093, 4094, 4095, 4096, 4097, 4098, 4099, 4100, 8192, 16384, 32767, 32768, 32769, 65535, 65536, _1M - 1
4582 };
4583 for (unsigned iMax = 0; iMax < RT_ELEMENTS(acbMax); iMax++)
4584 {
4585 size_t const cbMaxRead = acbMax[iMax];
4586 for (uint32_t iOffFromEos = 0; iOffFromEos < RT_ELEMENTS(aoffFromEos); iOffFromEos++)
4587 {
4588 uint32_t off = aoffFromEos[iOffFromEos];
4589 if (off >= cbMaxRead)
4590 continue;
4591 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile - off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
4592 size_t cbActual = ~(size_t)0;
4593 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, &cbActual), VINF_SUCCESS);
4594 RTTESTI_CHECK(cbActual == off);
4595
4596 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile - off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
4597 cbActual = ~(size_t)0;
4598 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, off, &cbActual), VINF_SUCCESS);
4599 RTTESTI_CHECK_MSG(cbActual == off, ("%#zx vs %#zx\n", cbActual, off));
4600
4601 cbActual = ~(size_t)0;
4602 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, 1, &cbActual), VINF_SUCCESS);
4603 RTTESTI_CHECK_MSG(cbActual == 0, ("cbActual=%zu\n", cbActual));
4604
4605 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, NULL), VERR_EOF);
4606
4607 /* Repeat using native APIs in case IPRT or other layers hide status codes: */
4608#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
4609 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile - off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
4610# ifdef RT_OS_OS2
4611 ULONG cbActual2 = ~(ULONG)0;
4612 APIRET orc = DosRead((HFILE)RTFileToNative(hFile1), pbBuf, cbMaxRead, &cbActual2);
4613 RTTESTI_CHECK_MSG(orc == NO_ERROR, ("orc=%u, expected 0\n", orc));
4614 RTTESTI_CHECK_MSG(cbActual2 == off, ("%#x vs %#x\n", cbActual2, off));
4615# else
4616 IO_STATUS_BLOCK const IosVirgin = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4617 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4618 NTSTATUS rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
4619 &Ios, pbBuf, (ULONG)cbMaxRead, NULL /*poffFile*/, NULL /*Key*/);
4620 if (off == 0)
4621 {
4622 RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE, ("rcNt=%#x, expected %#x\n", rcNt, STATUS_END_OF_FILE));
4623 RTTESTI_CHECK_MSG(Ios.Status == IosVirgin.Status /*slow?*/ || Ios.Status == STATUS_END_OF_FILE /*fastio?*/,
4624 ("%#x vs %x/%#x; off=%#x\n", Ios.Status, IosVirgin.Status, STATUS_END_OF_FILE, off));
4625 RTTESTI_CHECK_MSG(Ios.Information == IosVirgin.Information /*slow*/ || Ios.Information == 0 /*fastio?*/,
4626 ("%#zx vs %zx/0; off=%#x\n", Ios.Information, IosVirgin.Information, off));
4627 }
4628 else
4629 {
4630 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x, expected 0 (off=%#x cbMaxRead=%#zx)\n", rcNt, off, cbMaxRead));
4631 RTTESTI_CHECK_MSG(Ios.Status == STATUS_SUCCESS, ("%#x; off=%#x\n", Ios.Status, off));
4632 RTTESTI_CHECK_MSG(Ios.Information == off, ("%#zx vs %#x\n", Ios.Information, off));
4633 }
4634# endif
4635
4636# ifdef RT_OS_OS2
4637 cbActual2 = ~(ULONG)0;
4638 orc = DosRead((HFILE)RTFileToNative(hFile1), pbBuf, 1, &cbActual2);
4639 RTTESTI_CHECK_MSG(orc == NO_ERROR, ("orc=%u, expected 0\n", orc));
4640 RTTESTI_CHECK_MSG(cbActual2 == 0, ("cbActual2=%u\n", cbActual2));
4641# else
4642 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
4643 rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
4644 &Ios, pbBuf, 1, NULL /*poffFile*/, NULL /*Key*/);
4645 RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE, ("rcNt=%#x, expected %#x\n", rcNt, STATUS_END_OF_FILE));
4646# endif
4647
4648#endif
4649 }
4650 }
4651
4652 /*
4653 * Test reading beyond end of the file.
4654 */
4655 for (unsigned iMax = 0; iMax < RT_ELEMENTS(acbMax); iMax++)
4656 {
4657 size_t const cbMaxRead = acbMax[iMax];
4658 for (uint32_t off = 0; off < 256; off++)
4659 {
4660 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile + off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
4661 size_t cbActual = ~(size_t)0;
4662 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, &cbActual), VINF_SUCCESS);
4663 RTTESTI_CHECK(cbActual == 0);
4664
4665 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, cbMaxRead, NULL), VERR_EOF);
4666
4667 /* Repeat using native APIs in case IPRT or other layers hid status codes: */
4668#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
4669 RTTESTI_CHECK_RC(RTFileSeek(hFile1, cbFile + off, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
4670# ifdef RT_OS_OS2
4671 ULONG cbActual2 = ~(ULONG)0;
4672 APIRET orc = DosRead((HFILE)RTFileToNative(hFile1), pbBuf, cbMaxRead, &cbActual2);
4673 RTTESTI_CHECK_MSG(orc == NO_ERROR, ("orc=%u, expected 0\n", orc));
4674 RTTESTI_CHECK_MSG(cbActual2 == 0, ("%#x vs %#x\n", cbActual2, off));
4675# else
4676 IO_STATUS_BLOCK const IosVirgin = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4677 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4678 NTSTATUS rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
4679 &Ios, pbBuf, (ULONG)cbMaxRead, NULL /*poffFile*/, NULL /*Key*/);
4680 RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE, ("rcNt=%#x, expected %#x\n", rcNt, STATUS_END_OF_FILE));
4681 RTTESTI_CHECK_MSG(Ios.Status == IosVirgin.Status /*slow?*/ || Ios.Status == STATUS_END_OF_FILE /*fastio?*/,
4682 ("%#x vs %x/%#x; off=%#x\n", Ios.Status, IosVirgin.Status, STATUS_END_OF_FILE, off));
4683 RTTESTI_CHECK_MSG(Ios.Information == IosVirgin.Information /*slow*/ || Ios.Information == 0 /*fastio?*/,
4684 ("%#zx vs %zx/0; off=%#x\n", Ios.Information, IosVirgin.Information, off));
4685
4686 /* Need to work with sector size on uncached, but might be worth it for non-fastio path. */
4687 uint32_t cbSector = 0x1000;
4688 uint32_t off2 = off * cbSector + (cbFile & (cbSector - 1) ? cbSector - (cbFile & (cbSector - 1)) : 0);
4689 RTTESTI_CHECK_RC(RTFileSeek(hFileNoCache, cbFile + off2, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
4690 size_t const cbMaxRead2 = RT_ALIGN_Z(cbMaxRead, cbSector);
4691 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
4692 rcNt = NtReadFile((HANDLE)RTFileToNative(hFileNoCache), NULL /*hEvent*/, NULL /*ApcRoutine*/, NULL /*ApcContext*/,
4693 &Ios, pbBuf, (ULONG)cbMaxRead2, NULL /*poffFile*/, NULL /*Key*/);
4694 RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE,
4695 ("rcNt=%#x, expected %#x; off2=%x cbMaxRead2=%#x\n", rcNt, STATUS_END_OF_FILE, off2, cbMaxRead2));
4696 RTTESTI_CHECK_MSG(Ios.Status == IosVirgin.Status /*slow?*/,
4697 ("%#x vs %x; off2=%#x cbMaxRead2=%#x\n", Ios.Status, IosVirgin.Status, off2, cbMaxRead2));
4698 RTTESTI_CHECK_MSG(Ios.Information == IosVirgin.Information /*slow*/,
4699 ("%#zx vs %zx; off2=%#x cbMaxRead2=%#x\n", Ios.Information, IosVirgin.Information, off2, cbMaxRead2));
4700# endif
4701#endif
4702 }
4703 }
4704
4705 /*
4706 * Do uncached access, must be page aligned.
4707 */
4708 uint32_t cbPage = PAGE_SIZE;
4709 memset(pbBuf, 0x66, cbBuf);
4710 if (!g_fIgnoreNoCache || hFileNoCache != NIL_RTFILE)
4711 {
4712 RTTESTI_CHECK_RC(RTFileSeek(hFileNoCache, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
4713 for (size_t offBuf = 0; offBuf < cbBuf; )
4714 {
4715 uint32_t const cPagesLeft = (uint32_t)((cbBuf - offBuf) / cbPage);
4716 uint32_t const cPagesToRead = RTRandU32Ex(1, cPagesLeft);
4717 size_t const cbToRead = cPagesToRead * (size_t)cbPage;
4718 size_t cbActual = 0;
4719 RTTESTI_CHECK_RC(RTFileRead(hFileNoCache, &pbBuf[offBuf], cbToRead, &cbActual), VINF_SUCCESS);
4720 if (cbActual == cbToRead)
4721 offBuf += cbActual;
4722 else
4723 {
4724 RTTestIFailed("Attempting to read %#zx bytes at %#zx, only got %#x bytes back!\n", cbToRead, offBuf, cbActual);
4725 if (cbActual)
4726 offBuf += cbActual;
4727 else
4728 {
4729 memset(&pbBuf[offBuf], 0x11, cbPage);
4730 offBuf += cbPage;
4731 }
4732 }
4733 }
4734 fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf);
4735 }
4736
4737 /*
4738 * Check reading zero bytes at the end of the file.
4739 * Requires native call because RTFileWrite doesn't call kernel on zero byte reads.
4740 */
4741 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
4742# ifdef RT_OS_WINDOWS
4743 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4744 NTSTATUS rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 0, NULL, NULL);
4745 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
4746 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
4747 RTTESTI_CHECK(Ios.Information == 0);
4748
4749 IO_STATUS_BLOCK const IosVirgin = RTNT_IO_STATUS_BLOCK_INITIALIZER;
4750 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
4751 rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 1, NULL, NULL);
4752 RTTESTI_CHECK_MSG(rcNt == STATUS_END_OF_FILE, ("rcNt=%#x", rcNt));
4753 RTTESTI_CHECK_MSG(Ios.Status == IosVirgin.Status /*slow?*/ || Ios.Status == STATUS_END_OF_FILE /*fastio?*/,
4754 ("%#x vs %x/%#x\n", Ios.Status, IosVirgin.Status, STATUS_END_OF_FILE));
4755 RTTESTI_CHECK_MSG(Ios.Information == IosVirgin.Information /*slow*/ || Ios.Information == 0 /*fastio?*/,
4756 ("%#zx vs %zx/0\n", Ios.Information, IosVirgin.Information));
4757# else
4758 ssize_t cbRead = read((int)RTFileToNative(hFile1), pbBuf, 0);
4759 RTTESTI_CHECK(cbRead == 0);
4760# endif
4761
4762#else
4763 RT_NOREF(hFileNoCache);
4764#endif
4765
4766 /*
4767 * Scatter read function operation.
4768 */
4769#ifdef RT_OS_WINDOWS
4770 /** @todo RTFileSgReadAt is just a RTFileReadAt loop for windows NT. Need
4771 * to use ReadFileScatter (nocache + page aligned). */
4772#elif !defined(RT_OS_OS2) /** @todo implement RTFileSg using list i/o */
4773
4774# ifdef UIO_MAXIOV
4775 RTSGSEG aSegs[UIO_MAXIOV];
4776# else
4777 RTSGSEG aSegs[512];
4778# endif
4779 RTSGBUF SgBuf;
4780 uint32_t cIncr = 1;
4781 for (uint32_t cSegs = 1; cSegs <= RT_ELEMENTS(aSegs); cSegs += cIncr)
4782 {
4783 size_t const cbSeg = cbBuf / cSegs;
4784 size_t const cbToRead = cbSeg * cSegs;
4785 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
4786 {
4787 aSegs[iSeg].cbSeg = cbSeg;
4788 aSegs[iSeg].pvSeg = &pbBuf[cbToRead - (iSeg + 1) * cbSeg];
4789 }
4790 RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
4791 int rc = myFileSgReadAt(hFile1, 0, &SgBuf, cbToRead, NULL);
4792 if (RT_SUCCESS(rc))
4793 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
4794 {
4795 if (!fsPerfCheckReadBuf(__LINE__, iSeg * cbSeg, &pbBuf[cbToRead - (iSeg + 1) * cbSeg], cbSeg))
4796 {
4797 cSegs = RT_ELEMENTS(aSegs);
4798 break;
4799 }
4800 }
4801 else
4802 {
4803 RTTestIFailed("myFileSgReadAt failed: %Rrc - cSegs=%u cbSegs=%#zx cbToRead=%#zx", rc, cSegs, cbSeg, cbToRead);
4804 break;
4805 }
4806 if (cSegs == 16)
4807 cIncr = 7;
4808 else if (cSegs == 16 * 7 + 16 /*= 128*/)
4809 cIncr = 64;
4810 }
4811
4812 for (uint32_t iTest = 0; iTest < 128; iTest++)
4813 {
4814 uint32_t cSegs = RTRandU32Ex(1, RT_ELEMENTS(aSegs));
4815 uint32_t iZeroSeg = cSegs > 10 ? RTRandU32Ex(0, cSegs - 1) : UINT32_MAX / 2;
4816 uint32_t cZeroSegs = cSegs > 10 ? RTRandU32Ex(1, RT_MIN(cSegs - iZeroSeg, 25)) : 0;
4817 size_t cbToRead = 0;
4818 size_t cbLeft = cbBuf;
4819 uint8_t *pbCur = &pbBuf[cbBuf];
4820 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
4821 {
4822 uint32_t iAlign = RTRandU32Ex(0, 3);
4823 if (iAlign & 2) /* end is page aligned */
4824 {
4825 cbLeft -= (uintptr_t)pbCur & PAGE_OFFSET_MASK;
4826 pbCur -= (uintptr_t)pbCur & PAGE_OFFSET_MASK;
4827 }
4828
4829 size_t cbSegOthers = (cSegs - iSeg) * _8K;
4830 size_t cbSegMax = cbLeft > cbSegOthers ? cbLeft - cbSegOthers
4831 : cbLeft > cSegs ? cbLeft - cSegs
4832 : cbLeft;
4833 size_t cbSeg = cbLeft != 0 ? RTRandU32Ex(0, cbSegMax) : 0;
4834 if (iAlign & 1) /* start is page aligned */
4835 cbSeg += ((uintptr_t)pbCur - cbSeg) & PAGE_OFFSET_MASK;
4836
4837 if (iSeg - iZeroSeg < cZeroSegs)
4838 cbSeg = 0;
4839
4840 cbToRead += cbSeg;
4841 cbLeft -= cbSeg;
4842 pbCur -= cbSeg;
4843 aSegs[iSeg].cbSeg = cbSeg;
4844 aSegs[iSeg].pvSeg = pbCur;
4845 }
4846
4847 uint64_t offFile = cbToRead < cbFile ? RTRandU64Ex(0, cbFile - cbToRead) : 0;
4848 RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
4849 int rc = myFileSgReadAt(hFile1, offFile, &SgBuf, cbToRead, NULL);
4850 if (RT_SUCCESS(rc))
4851 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
4852 {
4853 if (!fsPerfCheckReadBuf(__LINE__, offFile, (uint8_t *)aSegs[iSeg].pvSeg, aSegs[iSeg].cbSeg))
4854 {
4855 RTTestIFailureDetails("iSeg=%#x cSegs=%#x cbSeg=%#zx cbToRead=%#zx\n", iSeg, cSegs, aSegs[iSeg].cbSeg, cbToRead);
4856 iTest = _16K;
4857 break;
4858 }
4859 offFile += aSegs[iSeg].cbSeg;
4860 }
4861 else
4862 {
4863 RTTestIFailed("myFileSgReadAt failed: %Rrc - cSegs=%#x cbToRead=%#zx", rc, cSegs, cbToRead);
4864 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
4865 RTTestIFailureDetails("aSeg[%u] = %p LB %#zx (last %p)\n", iSeg, aSegs[iSeg].pvSeg, aSegs[iSeg].cbSeg,
4866 (uint8_t *)aSegs[iSeg].pvSeg + aSegs[iSeg].cbSeg - 1);
4867 break;
4868 }
4869 }
4870
4871 /* reading beyond the end of the file */
4872 for (uint32_t cSegs = 1; cSegs < 6; cSegs++)
4873 for (uint32_t iTest = 0; iTest < 128; iTest++)
4874 {
4875 uint32_t const cbToRead = RTRandU32Ex(0, cbBuf);
4876 uint32_t const cbBeyond = cbToRead ? RTRandU32Ex(0, cbToRead) : 0;
4877 uint32_t const cbSeg = cbToRead / cSegs;
4878 uint32_t cbLeft = cbToRead;
4879 uint8_t *pbCur = &pbBuf[cbToRead];
4880 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
4881 {
4882 aSegs[iSeg].cbSeg = iSeg + 1 < cSegs ? cbSeg : cbLeft;
4883 aSegs[iSeg].pvSeg = pbCur -= aSegs[iSeg].cbSeg;
4884 cbLeft -= aSegs[iSeg].cbSeg;
4885 }
4886 Assert(pbCur == pbBuf);
4887
4888 uint64_t offFile = cbFile + cbBeyond - cbToRead;
4889 RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
4890 int rcExpect = cbBeyond == 0 || cbToRead == 0 ? VINF_SUCCESS : VERR_EOF;
4891 int rc = myFileSgReadAt(hFile1, offFile, &SgBuf, cbToRead, NULL);
4892 if (rc != rcExpect)
4893 {
4894 RTTestIFailed("myFileSgReadAt failed: %Rrc - cSegs=%#x cbToRead=%#zx cbBeyond=%#zx\n", rc, cSegs, cbToRead, cbBeyond);
4895 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
4896 RTTestIFailureDetails("aSeg[%u] = %p LB %#zx (last %p)\n", iSeg, aSegs[iSeg].pvSeg, aSegs[iSeg].cbSeg,
4897 (uint8_t *)aSegs[iSeg].pvSeg + aSegs[iSeg].cbSeg - 1);
4898 }
4899
4900 RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
4901 size_t cbActual = 0;
4902 rc = myFileSgReadAt(hFile1, offFile, &SgBuf, cbToRead, &cbActual);
4903 if (rc != VINF_SUCCESS || cbActual != cbToRead - cbBeyond)
4904 RTTestIFailed("myFileSgReadAt failed: %Rrc cbActual=%#zu - cSegs=%#x cbToRead=%#zx cbBeyond=%#zx expected %#zx\n",
4905 rc, cbActual, cSegs, cbToRead, cbBeyond, cbToRead - cbBeyond);
4906 if (RT_SUCCESS(rc) && cbActual > 0)
4907 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
4908 {
4909 if (!fsPerfCheckReadBuf(__LINE__, offFile, (uint8_t *)aSegs[iSeg].pvSeg, RT_MIN(cbActual, aSegs[iSeg].cbSeg)))
4910 {
4911 RTTestIFailureDetails("iSeg=%#x cSegs=%#x cbSeg=%#zx cbActual%#zx cbToRead=%#zx cbBeyond=%#zx\n",
4912 iSeg, cSegs, aSegs[iSeg].cbSeg, cbActual, cbToRead, cbBeyond);
4913 iTest = _16K;
4914 break;
4915 }
4916 if (cbActual <= aSegs[iSeg].cbSeg)
4917 break;
4918 cbActual -= aSegs[iSeg].cbSeg;
4919 offFile += aSegs[iSeg].cbSeg;
4920 }
4921 }
4922
4923#endif
4924
4925 /*
4926 * Other OS specific stuff.
4927 */
4928#ifdef RT_OS_WINDOWS
4929 /* Check that reading at an offset modifies the position: */
4930 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
4931 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile);
4932
4933 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
4934 LARGE_INTEGER offNt;
4935 offNt.QuadPart = cbFile / 2;
4936 rcNt = NtReadFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, _4K, &offNt, NULL);
4937 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
4938 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
4939 RTTESTI_CHECK(Ios.Information == _4K);
4940 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile / 2 + _4K);
4941 fsPerfCheckReadBuf(__LINE__, cbFile / 2, pbBuf, _4K);
4942#endif
4943
4944
4945 RTMemPageFree(pbBuf, cbBuf);
4946}
4947
4948
4949/**
4950 * One RTFileWrite profiling iteration.
4951 */
4952DECL_FORCE_INLINE(int) fsPerfIoWriteWorker(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock, uint8_t *pbBlock,
4953 uint64_t *poffActual, uint32_t *pcSeeks)
4954{
4955 /* Do we need to seek back to the start? */
4956 if (*poffActual + cbBlock <= cbFile)
4957 { /* likely */ }
4958 else
4959 {
4960 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
4961 *pcSeeks += 1;
4962 *poffActual = 0;
4963 }
4964
4965 size_t cbActuallyWritten = 0;
4966 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBlock, cbBlock, &cbActuallyWritten), VINF_SUCCESS, rcCheck);
4967 if (cbActuallyWritten == cbBlock)
4968 {
4969 *poffActual += cbActuallyWritten;
4970 return VINF_SUCCESS;
4971 }
4972 RTTestIFailed("RTFileWrite at %#RX64 returned just %#x bytes, expected %#x", *poffActual, cbActuallyWritten, cbBlock);
4973 *poffActual += cbActuallyWritten;
4974 return VERR_WRITE_ERROR;
4975}
4976
4977
4978void fsPerfIoWriteBlockSize(RTFILE hFile1, uint64_t cbFile, uint32_t cbBlock)
4979{
4980 RTTestISubF("IO - Sequential write %RU32", cbBlock);
4981
4982 if (cbBlock <= cbFile)
4983 {
4984 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBlock);
4985 if (pbBuf)
4986 {
4987 memset(pbBuf, 0xf7, cbBlock);
4988 PROFILE_IO_FN("RTFileWrite", fsPerfIoWriteWorker(hFile1, cbFile, cbBlock, pbBuf, &offActual, &cSeeks));
4989 RTMemPageFree(pbBuf, cbBlock);
4990 }
4991 else
4992 RTTestSkipped(g_hTest, "insufficient (virtual) memory available");
4993 }
4994 else
4995 RTTestSkipped(g_hTest, "test file too small");
4996}
4997
4998
4999/** pwritev is too new to be useful, so we use the writev api via this wrapper. */
5000DECLINLINE(int) myFileSgWriteAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToWrite, size_t *pcbWritten)
5001{
5002 int rc = RTFileSeek(hFile, off, RTFILE_SEEK_BEGIN, NULL);
5003 if (RT_SUCCESS(rc))
5004 rc = RTFileSgWrite(hFile, pSgBuf, cbToWrite, pcbWritten);
5005 return rc;
5006}
5007
5008
5009void fsPerfWrite(RTFILE hFile1, RTFILE hFileNoCache, RTFILE hFileWriteThru, uint64_t cbFile)
5010{
5011 RTTestISubF("IO - RTFileWrite");
5012
5013 /*
5014 * Allocate a big buffer we can play around with. Min size is 1MB.
5015 */
5016 size_t cbBuf = cbFile < _64M ? (size_t)cbFile : _64M;
5017 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
5018 while (!pbBuf)
5019 {
5020 cbBuf /= 2;
5021 RTTESTI_CHECK_RETV(cbBuf >= _1M);
5022 pbBuf = (uint8_t *)RTMemPageAlloc(_32M);
5023 }
5024
5025 uint8_t bFiller = 0x88;
5026
5027#if 1
5028 /*
5029 * Start at the beginning and write out the full buffer in random small chunks, thereby
5030 * checking that unaligned buffer addresses, size and file offsets work fine.
5031 */
5032 struct
5033 {
5034 uint64_t offFile;
5035 uint32_t cbMax;
5036 } aRuns[] = { { 0, 127 }, { cbFile - cbBuf, UINT32_MAX }, { 0, UINT32_MAX -1 }};
5037 for (uint32_t i = 0; i < RT_ELEMENTS(aRuns); i++, bFiller)
5038 {
5039 fsPerfFillWriteBuf(aRuns[i].offFile, pbBuf, cbBuf, bFiller);
5040 fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf, bFiller);
5041
5042 RTTESTI_CHECK_RC(RTFileSeek(hFile1, aRuns[i].offFile, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
5043 for (size_t offBuf = 0; offBuf < cbBuf; )
5044 {
5045 uint32_t const cbLeft = (uint32_t)(cbBuf - offBuf);
5046 uint32_t const cbToWrite = aRuns[i].cbMax < UINT32_MAX / 2 ? RTRandU32Ex(1, RT_MIN(aRuns[i].cbMax, cbLeft))
5047 : aRuns[i].cbMax == UINT32_MAX ? RTRandU32Ex(RT_MAX(cbLeft / 4, 1), cbLeft)
5048 : RTRandU32Ex(cbLeft >= _8K ? _8K : 1, RT_MIN(_1M, cbLeft));
5049 size_t cbActual = 0;
5050 RTTESTI_CHECK_RC(RTFileWrite(hFile1, &pbBuf[offBuf], cbToWrite, &cbActual), VINF_SUCCESS);
5051 if (cbActual == cbToWrite)
5052 {
5053 offBuf += cbActual;
5054 RTTESTI_CHECK_MSG(RTFileTell(hFile1) == aRuns[i].offFile + offBuf,
5055 ("%#RX64, expected %#RX64\n", RTFileTell(hFile1), aRuns[i].offFile + offBuf));
5056 }
5057 else
5058 {
5059 RTTestIFailed("Attempting to write %#x bytes at %#zx (%#x left), only got %#x written!\n",
5060 cbToWrite, offBuf, cbLeft, cbActual);
5061 if (cbActual)
5062 offBuf += cbActual;
5063 else
5064 pbBuf[offBuf++] = 0x11;
5065 }
5066 }
5067
5068 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, aRuns[i].offFile, pbBuf, cbBuf, NULL), VINF_SUCCESS);
5069 fsPerfCheckReadBuf(__LINE__, aRuns[i].offFile, pbBuf, cbBuf, bFiller);
5070 }
5071
5072
5073 /*
5074 * Do uncached and write-thru accesses, must be page aligned.
5075 */
5076 RTFILE ahFiles[2] = { hFileWriteThru, hFileNoCache };
5077 for (unsigned iFile = 0; iFile < RT_ELEMENTS(ahFiles); iFile++, bFiller++)
5078 {
5079 if (g_fIgnoreNoCache && ahFiles[iFile] == NIL_RTFILE)
5080 continue;
5081
5082 fsPerfFillWriteBuf(0, pbBuf, cbBuf, bFiller);
5083 fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf, bFiller);
5084 RTTESTI_CHECK_RC(RTFileSeek(ahFiles[iFile], 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
5085
5086 uint32_t cbPage = PAGE_SIZE;
5087 for (size_t offBuf = 0; offBuf < cbBuf; )
5088 {
5089 uint32_t const cPagesLeft = (uint32_t)((cbBuf - offBuf) / cbPage);
5090 uint32_t const cPagesToWrite = RTRandU32Ex(1, cPagesLeft);
5091 size_t const cbToWrite = cPagesToWrite * (size_t)cbPage;
5092 size_t cbActual = 0;
5093 RTTESTI_CHECK_RC(RTFileWrite(ahFiles[iFile], &pbBuf[offBuf], cbToWrite, &cbActual), VINF_SUCCESS);
5094 if (cbActual == cbToWrite)
5095 {
5096 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, offBuf, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
5097 fsPerfCheckReadBuf(__LINE__, offBuf, pbBuf, cbToWrite, bFiller);
5098 offBuf += cbActual;
5099 }
5100 else
5101 {
5102 RTTestIFailed("Attempting to read %#zx bytes at %#zx, only got %#x written!\n", cbToWrite, offBuf, cbActual);
5103 if (cbActual)
5104 offBuf += cbActual;
5105 else
5106 {
5107 memset(&pbBuf[offBuf], 0x11, cbPage);
5108 offBuf += cbPage;
5109 }
5110 }
5111 }
5112
5113 RTTESTI_CHECK_RC(RTFileReadAt(ahFiles[iFile], 0, pbBuf, cbBuf, NULL), VINF_SUCCESS);
5114 fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbBuf, bFiller);
5115 }
5116
5117 /*
5118 * Check the behavior of writing zero bytes to the file _4K from the end
5119 * using native API. In the olden days zero sized write have been known
5120 * to be used to truncate a file.
5121 */
5122 RTTESTI_CHECK_RC(RTFileSeek(hFile1, -_4K, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
5123# ifdef RT_OS_WINDOWS
5124 IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
5125 NTSTATUS rcNt = NtWriteFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, 0, NULL, NULL);
5126 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
5127 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
5128 RTTESTI_CHECK(Ios.Information == 0);
5129# else
5130 ssize_t cbWritten = write((int)RTFileToNative(hFile1), pbBuf, 0);
5131 RTTESTI_CHECK(cbWritten == 0);
5132# endif
5133 RTTESTI_CHECK_RC(RTFileRead(hFile1, pbBuf, _4K, NULL), VINF_SUCCESS);
5134 fsPerfCheckReadBuf(__LINE__, cbFile - _4K, pbBuf, _4K, pbBuf[0x8]);
5135
5136#else
5137 RT_NOREF(hFileNoCache, hFileWriteThru);
5138#endif
5139
5140 /*
5141 * Gather write function operation.
5142 */
5143#ifdef RT_OS_WINDOWS
5144 /** @todo RTFileSgWriteAt is just a RTFileWriteAt loop for windows NT. Need
5145 * to use WriteFileGather (nocache + page aligned). */
5146#elif !defined(RT_OS_OS2) /** @todo implement RTFileSg using list i/o */
5147
5148# ifdef UIO_MAXIOV
5149 RTSGSEG aSegs[UIO_MAXIOV];
5150# else
5151 RTSGSEG aSegs[512];
5152# endif
5153 RTSGBUF SgBuf;
5154 uint32_t cIncr = 1;
5155 for (uint32_t cSegs = 1; cSegs <= RT_ELEMENTS(aSegs); cSegs += cIncr, bFiller++)
5156 {
5157 size_t const cbSeg = cbBuf / cSegs;
5158 size_t const cbToWrite = cbSeg * cSegs;
5159 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
5160 {
5161 aSegs[iSeg].cbSeg = cbSeg;
5162 aSegs[iSeg].pvSeg = &pbBuf[cbToWrite - (iSeg + 1) * cbSeg];
5163 fsPerfFillWriteBuf(iSeg * cbSeg, (uint8_t *)aSegs[iSeg].pvSeg, cbSeg, bFiller);
5164 }
5165 RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
5166 int rc = myFileSgWriteAt(hFile1, 0, &SgBuf, cbToWrite, NULL);
5167 if (RT_SUCCESS(rc))
5168 {
5169 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, 0, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
5170 fsPerfCheckReadBuf(__LINE__, 0, pbBuf, cbToWrite, bFiller);
5171 }
5172 else
5173 {
5174 RTTestIFailed("myFileSgWriteAt failed: %Rrc - cSegs=%u cbSegs=%#zx cbToWrite=%#zx", rc, cSegs, cbSeg, cbToWrite);
5175 break;
5176 }
5177 if (cSegs == 16)
5178 cIncr = 7;
5179 else if (cSegs == 16 * 7 + 16 /*= 128*/)
5180 cIncr = 64;
5181 }
5182
5183 /* random stuff, including zero segments. */
5184 for (uint32_t iTest = 0; iTest < 128; iTest++, bFiller++)
5185 {
5186 uint32_t cSegs = RTRandU32Ex(1, RT_ELEMENTS(aSegs));
5187 uint32_t iZeroSeg = cSegs > 10 ? RTRandU32Ex(0, cSegs - 1) : UINT32_MAX / 2;
5188 uint32_t cZeroSegs = cSegs > 10 ? RTRandU32Ex(1, RT_MIN(cSegs - iZeroSeg, 25)) : 0;
5189 size_t cbToWrite = 0;
5190 size_t cbLeft = cbBuf;
5191 uint8_t *pbCur = &pbBuf[cbBuf];
5192 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
5193 {
5194 uint32_t iAlign = RTRandU32Ex(0, 3);
5195 if (iAlign & 2) /* end is page aligned */
5196 {
5197 cbLeft -= (uintptr_t)pbCur & PAGE_OFFSET_MASK;
5198 pbCur -= (uintptr_t)pbCur & PAGE_OFFSET_MASK;
5199 }
5200
5201 size_t cbSegOthers = (cSegs - iSeg) * _8K;
5202 size_t cbSegMax = cbLeft > cbSegOthers ? cbLeft - cbSegOthers
5203 : cbLeft > cSegs ? cbLeft - cSegs
5204 : cbLeft;
5205 size_t cbSeg = cbLeft != 0 ? RTRandU32Ex(0, cbSegMax) : 0;
5206 if (iAlign & 1) /* start is page aligned */
5207 cbSeg += ((uintptr_t)pbCur - cbSeg) & PAGE_OFFSET_MASK;
5208
5209 if (iSeg - iZeroSeg < cZeroSegs)
5210 cbSeg = 0;
5211
5212 cbToWrite += cbSeg;
5213 cbLeft -= cbSeg;
5214 pbCur -= cbSeg;
5215 aSegs[iSeg].cbSeg = cbSeg;
5216 aSegs[iSeg].pvSeg = pbCur;
5217 }
5218
5219 uint64_t const offFile = cbToWrite < cbFile ? RTRandU64Ex(0, cbFile - cbToWrite) : 0;
5220 uint64_t offFill = offFile;
5221 for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
5222 if (aSegs[iSeg].cbSeg)
5223 {
5224 fsPerfFillWriteBuf(offFill, (uint8_t *)aSegs[iSeg].pvSeg, aSegs[iSeg].cbSeg, bFiller);
5225 offFill += aSegs[iSeg].cbSeg;
5226 }
5227
5228 RTSgBufInit(&SgBuf, &aSegs[0], cSegs);
5229 int rc = myFileSgWriteAt(hFile1, offFile, &SgBuf, cbToWrite, NULL);
5230 if (RT_SUCCESS(rc))
5231 {
5232 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, offFile, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
5233 fsPerfCheckReadBuf(__LINE__, offFile, pbBuf, cbToWrite, bFiller);
5234 }
5235 else
5236 {
5237 RTTestIFailed("myFileSgWriteAt failed: %Rrc - cSegs=%#x cbToWrite=%#zx", rc, cSegs, cbToWrite);
5238 break;
5239 }
5240 }
5241
5242#endif
5243
5244 /*
5245 * Other OS specific stuff.
5246 */
5247#ifdef RT_OS_WINDOWS
5248 /* Check that reading at an offset modifies the position: */
5249 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, cbFile / 2, pbBuf, _4K, NULL), VINF_SUCCESS);
5250 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_END, NULL), VINF_SUCCESS);
5251 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile);
5252
5253 RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
5254 LARGE_INTEGER offNt;
5255 offNt.QuadPart = cbFile / 2;
5256 rcNt = NtWriteFile((HANDLE)RTFileToNative(hFile1), NULL, NULL, NULL, &Ios, pbBuf, _4K, &offNt, NULL);
5257 RTTESTI_CHECK_MSG(rcNt == STATUS_SUCCESS, ("rcNt=%#x", rcNt));
5258 RTTESTI_CHECK(Ios.Status == STATUS_SUCCESS);
5259 RTTESTI_CHECK(Ios.Information == _4K);
5260 RTTESTI_CHECK(RTFileTell(hFile1) == cbFile / 2 + _4K);
5261#endif
5262
5263 RTMemPageFree(pbBuf, cbBuf);
5264}
5265
5266
5267/**
5268 * Worker for testing RTFileFlush.
5269 */
5270DECL_FORCE_INLINE(int) fsPerfFSyncWorker(RTFILE hFile1, uint64_t cbFile, uint8_t *pbBuf, size_t cbBuf, uint64_t *poffFile)
5271{
5272 if (*poffFile + cbBuf <= cbFile)
5273 { /* likely */ }
5274 else
5275 {
5276 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
5277 *poffFile = 0;
5278 }
5279
5280 RTTESTI_CHECK_RC_RET(RTFileWrite(hFile1, pbBuf, cbBuf, NULL), VINF_SUCCESS, rcCheck);
5281 RTTESTI_CHECK_RC_RET(RTFileFlush(hFile1), VINF_SUCCESS, rcCheck);
5282
5283 *poffFile += cbBuf;
5284 return VINF_SUCCESS;
5285}
5286
5287
5288void fsPerfFSync(RTFILE hFile1, uint64_t cbFile)
5289{
5290 RTTestISub("fsync");
5291
5292 RTTESTI_CHECK_RC(RTFileFlush(hFile1), VINF_SUCCESS);
5293
5294 PROFILE_FN(RTFileFlush(hFile1), g_nsTestRun, "RTFileFlush");
5295
5296 size_t cbBuf = PAGE_SIZE;
5297 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
5298 RTTESTI_CHECK_RETV(pbBuf != NULL);
5299 memset(pbBuf, 0xf4, cbBuf);
5300
5301 RTTESTI_CHECK_RC(RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
5302 uint64_t offFile = 0;
5303 PROFILE_FN(fsPerfFSyncWorker(hFile1, cbFile, pbBuf, cbBuf, &offFile), g_nsTestRun, "RTFileWrite[Page]/RTFileFlush");
5304
5305 RTMemPageFree(pbBuf, cbBuf);
5306}
5307
5308
5309#ifndef RT_OS_OS2
5310/**
5311 * Worker for profiling msync.
5312 */
5313DECL_FORCE_INLINE(int) fsPerfMSyncWorker(uint8_t *pbMapping, size_t offMapping, size_t cbFlush, size_t *pcbFlushed)
5314{
5315 uint8_t *pbCur = &pbMapping[offMapping];
5316 for (size_t offFlush = 0; offFlush < cbFlush; offFlush += PAGE_SIZE)
5317 *(size_t volatile *)&pbCur[offFlush + 8] = cbFlush;
5318# ifdef RT_OS_WINDOWS
5319 RTTESTI_CHECK(FlushViewOfFile(pbCur, cbFlush));
5320# else
5321 RTTESTI_CHECK(msync(pbCur, cbFlush, MS_SYNC) == 0);
5322# endif
5323 if (*pcbFlushed < offMapping + cbFlush)
5324 *pcbFlushed = offMapping + cbFlush;
5325 return VINF_SUCCESS;
5326}
5327#endif /* !RT_OS_OS2 */
5328
5329
5330void fsPerfMMap(RTFILE hFile1, RTFILE hFileNoCache, uint64_t cbFile)
5331{
5332 RTTestISub("mmap");
5333#if !defined(RT_OS_OS2)
5334 static const char * const s_apszStates[] = { "readonly", "writecopy", "readwrite" };
5335 enum { kMMap_ReadOnly = 0, kMMap_WriteCopy, kMMap_ReadWrite, kMMap_End };
5336 for (int enmState = kMMap_ReadOnly; enmState < kMMap_End; enmState++)
5337 {
5338 /*
5339 * Do the mapping.
5340 */
5341 size_t cbMapping = (size_t)cbFile;
5342 if (cbMapping != cbFile)
5343 cbMapping = _256M;
5344 uint8_t *pbMapping;
5345
5346# ifdef RT_OS_WINDOWS
5347 HANDLE hSection;
5348 pbMapping = NULL;
5349 for (;; cbMapping /= 2)
5350 {
5351 hSection = CreateFileMapping((HANDLE)RTFileToNative(hFile1), NULL,
5352 enmState == kMMap_ReadOnly ? PAGE_READONLY
5353 : enmState == kMMap_WriteCopy ? PAGE_WRITECOPY : PAGE_READWRITE,
5354 (uint32_t)((uint64_t)cbMapping >> 32), (uint32_t)cbMapping, NULL);
5355 DWORD dwErr1 = GetLastError();
5356 DWORD dwErr2 = 0;
5357 if (hSection != NULL)
5358 {
5359 pbMapping = (uint8_t *)MapViewOfFile(hSection,
5360 enmState == kMMap_ReadOnly ? FILE_MAP_READ
5361 : enmState == kMMap_WriteCopy ? FILE_MAP_COPY
5362 : FILE_MAP_WRITE,
5363 0, 0, cbMapping);
5364 if (pbMapping)
5365 break;
5366 dwErr2 = GetLastError();
5367 CloseHandle(hSection);
5368 }
5369 if (cbMapping <= _2M)
5370 {
5371 RTTestIFailed("%u/%s: CreateFileMapping or MapViewOfFile failed: %u, %u",
5372 enmState, s_apszStates[enmState], dwErr1, dwErr2);
5373 break;
5374 }
5375 }
5376# else
5377 for (;; cbMapping /= 2)
5378 {
5379 pbMapping = (uint8_t *)mmap(NULL, cbMapping,
5380 enmState == kMMap_ReadOnly ? PROT_READ : PROT_READ | PROT_WRITE,
5381 enmState == kMMap_WriteCopy ? MAP_PRIVATE : MAP_SHARED,
5382 (int)RTFileToNative(hFile1), 0);
5383 if ((void *)pbMapping != MAP_FAILED)
5384 break;
5385 if (cbMapping <= _2M)
5386 {
5387 RTTestIFailed("%u/%s: mmap failed: %s (%u)", enmState, s_apszStates[enmState], strerror(errno), errno);
5388 break;
5389 }
5390 }
5391# endif
5392 if (cbMapping <= _2M)
5393 continue;
5394
5395 /*
5396 * Time page-ins just for fun.
5397 */
5398 size_t const cPages = cbMapping >> PAGE_SHIFT;
5399 size_t uDummy = 0;
5400 uint64_t ns = RTTimeNanoTS();
5401 for (size_t iPage = 0; iPage < cPages; iPage++)
5402 uDummy += ASMAtomicReadU8(&pbMapping[iPage << PAGE_SHIFT]);
5403 ns = RTTimeNanoTS() - ns;
5404 RTTestIValueF(ns / cPages, RTTESTUNIT_NS_PER_OCCURRENCE, "page-in %s", s_apszStates[enmState]);
5405
5406 /* Check the content. */
5407 fsPerfCheckReadBuf(__LINE__, 0, pbMapping, cbMapping);
5408
5409 if (enmState != kMMap_ReadOnly)
5410 {
5411 /* Write stuff to the first two megabytes. In the COW case, we'll detect
5412 corruption of shared data during content checking of the RW iterations. */
5413 fsPerfFillWriteBuf(0, pbMapping, _2M, 0xf7);
5414 if (enmState == kMMap_ReadWrite)
5415 {
5416 /* For RW we can try read back from the file handle and check if we get
5417 a match there first. */
5418 uint8_t abBuf[_4K];
5419 for (uint32_t off = 0; off < _2M; off += sizeof(abBuf))
5420 {
5421 RTTESTI_CHECK_RC(RTFileReadAt(hFile1, off, abBuf, sizeof(abBuf), NULL), VINF_SUCCESS);
5422 fsPerfCheckReadBuf(__LINE__, off, abBuf, sizeof(abBuf), 0xf7);
5423 }
5424# ifdef RT_OS_WINDOWS
5425 RTTESTI_CHECK(FlushViewOfFile(pbMapping, _2M));
5426# else
5427 RTTESTI_CHECK(msync(pbMapping, _2M, MS_SYNC) == 0);
5428# endif
5429
5430 /*
5431 * Time modifying and flushing a few different number of pages.
5432 */
5433 static size_t const s_acbFlush[] = { PAGE_SIZE, PAGE_SIZE * 2, PAGE_SIZE * 3, PAGE_SIZE * 8, PAGE_SIZE * 16, _2M };
5434 for (unsigned iFlushSize = 0 ; iFlushSize < RT_ELEMENTS(s_acbFlush); iFlushSize++)
5435 {
5436 size_t const cbFlush = s_acbFlush[iFlushSize];
5437 if (cbFlush > cbMapping)
5438 continue;
5439
5440 char szDesc[80];
5441 RTStrPrintf(szDesc, sizeof(szDesc), "touch/flush/%zu", cbFlush);
5442 size_t const cFlushes = cbMapping / cbFlush;
5443 size_t const cbMappingUsed = cFlushes * cbFlush;
5444 size_t cbFlushed = 0;
5445 PROFILE_FN(fsPerfMSyncWorker(pbMapping, (iIteration * cbFlush) % cbMappingUsed, cbFlush, &cbFlushed),
5446 g_nsTestRun, szDesc);
5447
5448 /*
5449 * Check that all the changes made it thru to the file:
5450 */
5451 if (!g_fIgnoreNoCache || hFileNoCache != NIL_RTFILE)
5452 {
5453 size_t cbBuf = _2M;
5454 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
5455 if (!pbBuf)
5456 {
5457 cbBuf = _4K;
5458 pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
5459 }
5460 RTTESTI_CHECK(pbBuf != NULL);
5461 if (pbBuf)
5462 {
5463 RTTESTI_CHECK_RC(RTFileSeek(hFileNoCache, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
5464 size_t const cbToCheck = RT_MIN(cFlushes * cbFlush, cbFlushed);
5465 unsigned cErrors = 0;
5466 for (size_t offBuf = 0; cErrors < 32 && offBuf < cbToCheck; offBuf += cbBuf)
5467 {
5468 size_t cbToRead = RT_MIN(cbBuf, cbToCheck - offBuf);
5469 RTTESTI_CHECK_RC(RTFileRead(hFileNoCache, pbBuf, cbToRead, NULL), VINF_SUCCESS);
5470
5471 for (size_t offFlush = 0; offFlush < cbToRead; offFlush += PAGE_SIZE)
5472 if (*(size_t volatile *)&pbBuf[offFlush + 8] != cbFlush)
5473 {
5474 RTTestIFailed("Flush issue at offset #%zx: %#zx, expected %#zx (cbFlush=%#zx, %#RX64)",
5475 offBuf + offFlush + 8, *(size_t volatile *)&pbBuf[offFlush + 8],
5476 cbFlush, cbFlush, *(uint64_t volatile *)&pbBuf[offFlush]);
5477 if (++cErrors > 32)
5478 break;
5479 }
5480 }
5481 RTMemPageFree(pbBuf, cbBuf);
5482 }
5483 }
5484 }
5485
5486# if 0 /* not needed, very very slow */
5487 /*
5488 * Restore the file to 0xf6 state for the next test.
5489 */
5490 RTTestIPrintf(RTTESTLVL_ALWAYS, "Restoring content...\n");
5491 fsPerfFillWriteBuf(0, pbMapping, cbMapping, 0xf6);
5492# ifdef RT_OS_WINDOWS
5493 RTTESTI_CHECK(FlushViewOfFile(pbMapping, cbMapping));
5494# else
5495 RTTESTI_CHECK(msync(pbMapping, cbMapping, MS_SYNC) == 0);
5496# endif
5497 RTTestIPrintf(RTTESTLVL_ALWAYS, "... done\n");
5498# endif
5499 }
5500 }
5501
5502 /*
5503 * Observe how regular writes affects a read-only or readwrite mapping.
5504 * These should ideally be immediately visible in the mapping, at least
5505 * when not performed thru an no-cache handle.
5506 */
5507 if (enmState == kMMap_ReadOnly || enmState == kMMap_ReadWrite)
5508 {
5509 size_t cbBuf = RT_MIN(_2M, cbMapping / 2);
5510 uint8_t *pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
5511 if (!pbBuf)
5512 {
5513 cbBuf = _4K;
5514 pbBuf = (uint8_t *)RTMemPageAlloc(cbBuf);
5515 }
5516 RTTESTI_CHECK(pbBuf != NULL);
5517 if (pbBuf)
5518 {
5519 /* Do a number of random writes to the file (using hFile1).
5520 Immediately undoing them. */
5521 for (uint32_t i = 0; i < 128; i++)
5522 {
5523 /* Generate a randomly sized write at a random location, making
5524 sure it differs from whatever is there already before writing. */
5525 uint32_t const cbToWrite = RTRandU32Ex(1, (uint32_t)cbBuf);
5526 uint64_t const offToWrite = RTRandU64Ex(0, cbMapping - cbToWrite);
5527
5528 fsPerfFillWriteBuf(offToWrite, pbBuf, cbToWrite, 0xf8);
5529 pbBuf[0] = ~pbBuf[0];
5530 if (cbToWrite > 1)
5531 pbBuf[cbToWrite - 1] = ~pbBuf[cbToWrite - 1];
5532 RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, offToWrite, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
5533
5534 /* Check the mapping. */
5535 if (memcmp(&pbMapping[(size_t)offToWrite], pbBuf, cbToWrite) != 0)
5536 {
5537 RTTestIFailed("Write #%u @ %#RX64 LB %#x was not reflected in the mapping!\n", i, offToWrite, cbToWrite);
5538 }
5539
5540 /* Restore */
5541 fsPerfFillWriteBuf(offToWrite, pbBuf, cbToWrite, 0xf6);
5542 RTTESTI_CHECK_RC(RTFileWriteAt(hFile1, offToWrite, pbBuf, cbToWrite, NULL), VINF_SUCCESS);
5543 }
5544
5545 RTMemPageFree(pbBuf, cbBuf);
5546 }
5547 }
5548
5549 /*
5550 * Unmap it.
5551 */
5552# ifdef RT_OS_WINDOWS
5553 RTTESTI_CHECK(UnmapViewOfFile(pbMapping));
5554 RTTESTI_CHECK(CloseHandle(hSection));
5555# else
5556 RTTESTI_CHECK(munmap(pbMapping, cbMapping) == 0);
5557# endif
5558 }
5559
5560 /*
5561 * Memory mappings without open handles (pretty common).
5562 */
5563 for (uint32_t i = 0; i < 32; i++)
5564 {
5565 /* Create a new file, 256 KB in size, and fill it with random bytes.
5566 Try uncached access if we can to force the page-in to do actual reads. */
5567 char szFile2[FSPERF_MAX_PATH + 32];
5568 memcpy(szFile2, g_szDir, g_cchDir);
5569 RTStrPrintf(&szFile2[g_cchDir], sizeof(szFile2) - g_cchDir, "mmap-%u.noh", i);
5570 RTFILE hFile2 = NIL_RTFILE;
5571 int rc = (i & 3) == 3 ? VERR_TRY_AGAIN
5572 : RTFileOpen(&hFile2, szFile2, RTFILE_O_READWRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_NO_CACHE);
5573 if (RT_FAILURE(rc))
5574 {
5575 RTTESTI_CHECK_RC_BREAK(RTFileOpen(&hFile2, szFile2, RTFILE_O_READWRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE),
5576 VINF_SUCCESS);
5577 }
5578
5579 static char s_abContentUnaligned[256*1024 + PAGE_SIZE - 1];
5580 char * const pbContent = &s_abContentUnaligned[PAGE_SIZE - ((uintptr_t)&s_abContentUnaligned[0] & PAGE_OFFSET_MASK)];
5581 size_t const cbContent = 256*1024;
5582 RTRandBytes(pbContent, cbContent);
5583 RTTESTI_CHECK_RC(rc = RTFileWrite(hFile2, pbContent, cbContent, NULL), VINF_SUCCESS);
5584 RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
5585 if (RT_SUCCESS(rc))
5586 {
5587 /* Reopen the file with normal caching. Every second time, we also
5588 does a read-only open of it to confuse matters. */
5589 RTFILE hFile3 = NIL_RTFILE;
5590 if ((i & 3) == 3)
5591 RTTESTI_CHECK_RC(RTFileOpen(&hFile3, szFile2, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE), VINF_SUCCESS);
5592 hFile2 = NIL_RTFILE;
5593 RTTESTI_CHECK_RC_BREAK(RTFileOpen(&hFile2, szFile2, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE),
5594 VINF_SUCCESS);
5595 if ((i & 3) == 1)
5596 RTTESTI_CHECK_RC(RTFileOpen(&hFile3, szFile2, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE), VINF_SUCCESS);
5597
5598 /* Memory map it read-write (no COW). */
5599#ifdef RT_OS_WINDOWS
5600 HANDLE hSection = CreateFileMapping((HANDLE)RTFileToNative(hFile2), NULL, PAGE_READWRITE, 0, cbContent, NULL);
5601 RTTESTI_CHECK_MSG(hSection != NULL, ("last error %u\n", GetLastError));
5602 uint8_t *pbMapping = (uint8_t *)MapViewOfFile(hSection, FILE_MAP_WRITE, 0, 0, cbContent);
5603 RTTESTI_CHECK_MSG(pbMapping != NULL, ("last error %u\n", GetLastError));
5604 RTTESTI_CHECK_MSG(CloseHandle(hSection), ("last error %u\n", GetLastError));
5605# else
5606 uint8_t *pbMapping = (uint8_t *)mmap(NULL, cbContent, PROT_READ | PROT_WRITE, MAP_SHARED,
5607 (int)RTFileToNative(hFile2), 0);
5608 if ((void *)pbMapping == MAP_FAILED)
5609 pbMapping = NULL;
5610 RTTESTI_CHECK_MSG(pbMapping != NULL, ("errno=%s (%d)\n", strerror(errno), errno));
5611# endif
5612
5613 /* Close the file handles. */
5614 if ((i & 7) == 7)
5615 {
5616 RTTESTI_CHECK_RC(RTFileClose(hFile3), VINF_SUCCESS);
5617 hFile3 = NIL_RTFILE;
5618 }
5619 RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
5620 if ((i & 7) == 5)
5621 {
5622 RTTESTI_CHECK_RC(RTFileClose(hFile3), VINF_SUCCESS);
5623 hFile3 = NIL_RTFILE;
5624 }
5625 if (pbMapping)
5626 {
5627 RTThreadSleep(2); /* fudge for cleanup/whatever */
5628
5629 /* Page in the mapping by comparing with the content we wrote above. */
5630 RTTESTI_CHECK(memcmp(pbMapping, pbContent, cbContent) == 0);
5631
5632 /* Now dirty everything by inverting everything. */
5633 size_t *puCur = (size_t *)pbMapping;
5634 size_t cLeft = cbContent / sizeof(*puCur);
5635 while (cLeft-- > 0)
5636 {
5637 *puCur = ~*puCur;
5638 puCur++;
5639 }
5640
5641 /* Sync it all. */
5642# ifdef RT_OS_WINDOWS
5643 RTTESTI_CHECK(FlushViewOfFile(pbMapping, cbContent));
5644# else
5645 RTTESTI_CHECK(msync(pbMapping, cbContent, MS_SYNC) == 0);
5646# endif
5647
5648 /* Unmap it. */
5649# ifdef RT_OS_WINDOWS
5650 RTTESTI_CHECK(UnmapViewOfFile(pbMapping));
5651# else
5652 RTTESTI_CHECK(munmap(pbMapping, cbContent) == 0);
5653# endif
5654 }
5655
5656 if (hFile3 != NIL_RTFILE)
5657 RTTESTI_CHECK_RC(RTFileClose(hFile3), VINF_SUCCESS);
5658 }
5659 RTTESTI_CHECK_RC(RTFileDelete(szFile2), VINF_SUCCESS);
5660 }
5661
5662
5663#else
5664 RTTestSkipped(g_hTest, "not supported/implemented");
5665 RT_NOREF(hFile1, hFileNoCache, cbFile);
5666#endif
5667}
5668
5669
5670/**
5671 * This does the read, write and seek tests.
5672 */
5673void fsPerfIo(void)
5674{
5675 RTTestISub("I/O");
5676
5677 /*
5678 * Determin the size of the test file.
5679 */
5680 g_szDir[g_cchDir] = '\0';
5681 RTFOFF cbFree = 0;
5682 RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
5683 uint64_t cbFile = g_cbIoFile;
5684 if (cbFile + _16M < (uint64_t)cbFree)
5685 cbFile = RT_ALIGN_64(cbFile, _64K);
5686 else if (cbFree < _32M)
5687 {
5688 RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 32MB", cbFree);
5689 return;
5690 }
5691 else
5692 {
5693 cbFile = cbFree - (cbFree > _128M ? _64M : _16M);
5694 cbFile = RT_ALIGN_64(cbFile, _64K);
5695 RTTestIPrintf(RTTESTLVL_ALWAYS, "Adjusted file size to %'RU64 bytes, due to %'RU64 bytes free.\n", cbFile, cbFree);
5696 }
5697 if (cbFile < _64K)
5698 {
5699 RTTestSkipped(g_hTest, "Specified test file size too small: %'RU64 bytes, requires >= 64KB", cbFile);
5700 return;
5701 }
5702
5703 /*
5704 * Create a cbFile sized test file.
5705 */
5706 RTFILE hFile1;
5707 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file21")),
5708 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
5709 RTFILE hFileNoCache;
5710 if (!g_fIgnoreNoCache)
5711 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFileNoCache, g_szDir,
5712 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE | RTFILE_O_NO_CACHE),
5713 VINF_SUCCESS);
5714 else
5715 {
5716 int rc = RTFileOpen(&hFileNoCache, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE | RTFILE_O_NO_CACHE);
5717 if (RT_FAILURE(rc))
5718 {
5719 RTTestIPrintf(RTTESTLVL_ALWAYS, "Unable to open I/O file with non-cache flag (%Rrc), skipping related tests.\n", rc);
5720 hFileNoCache = NIL_RTFILE;
5721 }
5722 }
5723 RTFILE hFileWriteThru;
5724 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFileWriteThru, g_szDir,
5725 RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE | RTFILE_O_WRITE_THROUGH),
5726 VINF_SUCCESS);
5727
5728 uint8_t *pbFree = NULL;
5729 int rc = fsPerfIoPrepFile(hFile1, cbFile, &pbFree);
5730 RTMemFree(pbFree);
5731 if (RT_SUCCESS(rc))
5732 {
5733 /*
5734 * Do the testing & profiling.
5735 */
5736 if (g_fSeek)
5737 fsPerfIoSeek(hFile1, cbFile);
5738
5739 if (g_fReadTests)
5740 fsPerfRead(hFile1, hFileNoCache, cbFile);
5741 if (g_fReadPerf)
5742 for (unsigned i = 0; i < g_cIoBlocks; i++)
5743 fsPerfIoReadBlockSize(hFile1, cbFile, g_acbIoBlocks[i]);
5744#ifdef FSPERF_TEST_SENDFILE
5745 if (g_fSendFile)
5746 fsPerfSendFile(hFile1, cbFile);
5747#endif
5748#ifdef RT_OS_LINUX
5749 if (g_fSplice)
5750 fsPerfSpliceToPipe(hFile1, cbFile);
5751#endif
5752 if (g_fMMap)
5753 fsPerfMMap(hFile1, hFileNoCache, cbFile);
5754
5755 /* This is destructive to the file content. */
5756 if (g_fWriteTests)
5757 fsPerfWrite(hFile1, hFileNoCache, hFileWriteThru, cbFile);
5758 if (g_fWritePerf)
5759 for (unsigned i = 0; i < g_cIoBlocks; i++)
5760 fsPerfIoWriteBlockSize(hFile1, cbFile, g_acbIoBlocks[i]);
5761#ifdef RT_OS_LINUX
5762 if (g_fSplice)
5763 fsPerfSpliceToFile(hFile1, cbFile);
5764#endif
5765 if (g_fFSync)
5766 fsPerfFSync(hFile1, cbFile);
5767 }
5768
5769 RTTESTI_CHECK_RC(RTFileSetSize(hFile1, 0), VINF_SUCCESS);
5770 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
5771 if (hFileNoCache != NIL_RTFILE || !g_fIgnoreNoCache)
5772 RTTESTI_CHECK_RC(RTFileClose(hFileNoCache), VINF_SUCCESS);
5773 RTTESTI_CHECK_RC(RTFileClose(hFileWriteThru), VINF_SUCCESS);
5774 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
5775}
5776
5777
5778DECL_FORCE_INLINE(int) fsPerfCopyWorker1(const char *pszSrc, const char *pszDst)
5779{
5780 RTFileDelete(pszDst);
5781 return RTFileCopy(pszSrc, pszDst);
5782}
5783
5784
5785#ifdef RT_OS_LINUX
5786DECL_FORCE_INLINE(int) fsPerfCopyWorkerSendFile(RTFILE hFile1, RTFILE hFile2, size_t cbFile)
5787{
5788 RTTESTI_CHECK_RC_RET(RTFileSeek(hFile2, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS, rcCheck);
5789
5790 loff_t off = 0;
5791 ssize_t cbSent = sendfile((int)RTFileToNative(hFile2), (int)RTFileToNative(hFile1), &off, cbFile);
5792 if (cbSent > 0 && (size_t)cbSent == cbFile)
5793 return 0;
5794
5795 int rc = VERR_GENERAL_FAILURE;
5796 if (cbSent < 0)
5797 {
5798 rc = RTErrConvertFromErrno(errno);
5799 RTTestIFailed("sendfile(file,file,NULL,%#zx) failed (%zd): %d (%Rrc)", cbFile, cbSent, errno, rc);
5800 }
5801 else
5802 RTTestIFailed("sendfile(file,file,NULL,%#zx) returned %#zx, expected %#zx (diff %zd)",
5803 cbFile, cbSent, cbFile, cbSent - cbFile);
5804 return rc;
5805}
5806#endif /* RT_OS_LINUX */
5807
5808
5809static void fsPerfCopy(void)
5810{
5811 RTTestISub("copy");
5812
5813 /*
5814 * Non-existing files.
5815 */
5816 RTTESTI_CHECK_RC(RTFileCopy(InEmptyDir(RT_STR_TUPLE("no-such-file")),
5817 InDir2(RT_STR_TUPLE("whatever"))), VERR_FILE_NOT_FOUND);
5818 RTTESTI_CHECK_RC(RTFileCopy(InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file")),
5819 InDir2(RT_STR_TUPLE("no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
5820 RTTESTI_CHECK_RC(RTFileCopy(InDir(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file")),
5821 InDir2(RT_STR_TUPLE("whatever"))), VERR_PATH_NOT_FOUND);
5822
5823 RTTESTI_CHECK_RC(RTFileCopy(InDir(RT_STR_TUPLE("known-file")),
5824 InEmptyDir(RT_STR_TUPLE("no-such-dir" RTPATH_SLASH_STR "no-such-file"))), FSPERF_VERR_PATH_NOT_FOUND);
5825 RTTESTI_CHECK_RC(RTFileCopy(InDir(RT_STR_TUPLE("known-file")),
5826 InDir2(RT_STR_TUPLE("known-file" RTPATH_SLASH_STR "no-such-file"))), VERR_PATH_NOT_FOUND);
5827
5828 /*
5829 * Determin the size of the test file.
5830 * We want to be able to make 1 copy of it.
5831 */
5832 g_szDir[g_cchDir] = '\0';
5833 RTFOFF cbFree = 0;
5834 RTTESTI_CHECK_RC_RETV(RTFsQuerySizes(g_szDir, NULL, &cbFree, NULL, NULL), VINF_SUCCESS);
5835 uint64_t cbFile = g_cbIoFile;
5836 if (cbFile + _16M < (uint64_t)cbFree)
5837 cbFile = RT_ALIGN_64(cbFile, _64K);
5838 else if (cbFree < _32M)
5839 {
5840 RTTestSkipped(g_hTest, "Insufficent free space: %'RU64 bytes, requires >= 32MB", cbFree);
5841 return;
5842 }
5843 else
5844 {
5845 cbFile = cbFree - (cbFree > _128M ? _64M : _16M);
5846 cbFile = RT_ALIGN_64(cbFile, _64K);
5847 RTTestIPrintf(RTTESTLVL_ALWAYS, "Adjusted file size to %'RU64 bytes, due to %'RU64 bytes free.\n", cbFile, cbFree);
5848 }
5849 if (cbFile < _512K * 2)
5850 {
5851 RTTestSkipped(g_hTest, "Specified test file size too small: %'RU64 bytes, requires >= 1MB", cbFile);
5852 return;
5853 }
5854 cbFile /= 2;
5855
5856 /*
5857 * Create a cbFile sized test file.
5858 */
5859 RTFILE hFile1;
5860 RTTESTI_CHECK_RC_RETV(RTFileOpen(&hFile1, InDir(RT_STR_TUPLE("file22")),
5861 RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_READWRITE), VINF_SUCCESS);
5862 uint8_t *pbFree = NULL;
5863 int rc = fsPerfIoPrepFile(hFile1, cbFile, &pbFree);
5864 RTMemFree(pbFree);
5865 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
5866 if (RT_SUCCESS(rc))
5867 {
5868 /*
5869 * Make copies.
5870 */
5871 /* plain */
5872 RTFileDelete(InDir2(RT_STR_TUPLE("file23")));
5873 RTTESTI_CHECK_RC(RTFileCopy(g_szDir, g_szDir2), VINF_SUCCESS);
5874 RTTESTI_CHECK_RC(RTFileCopy(g_szDir, g_szDir2), VERR_ALREADY_EXISTS);
5875 RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
5876
5877 /* by handle */
5878 hFile1 = NIL_RTFILE;
5879 RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
5880 RTFILE hFile2 = NIL_RTFILE;
5881 RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
5882 RTTESTI_CHECK_RC(RTFileCopyByHandles(hFile1, hFile2), VINF_SUCCESS);
5883 RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
5884 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
5885 RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
5886
5887 /* copy part */
5888 hFile1 = NIL_RTFILE;
5889 RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
5890 hFile2 = NIL_RTFILE;
5891 RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
5892 RTTESTI_CHECK_RC(RTFileCopyPart(hFile1, 0, hFile2, 0, cbFile / 2, 0, NULL), VINF_SUCCESS);
5893 RTTESTI_CHECK_RC(RTFileCopyPart(hFile1, cbFile / 2, hFile2, cbFile / 2, cbFile - cbFile / 2, 0, NULL), VINF_SUCCESS);
5894 RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
5895 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
5896 RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
5897
5898#ifdef RT_OS_LINUX
5899 /*
5900 * On linux we can also use sendfile between two files, except for 2.5.x to 2.6.33.
5901 */
5902 uint64_t const cbFileMax = RT_MIN(cbFile, UINT32_C(0x7ffff000));
5903 char szRelease[64];
5904 RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szRelease, sizeof(szRelease));
5905 bool const fSendFileBetweenFiles = RTStrVersionCompare(szRelease, "2.5.0") < 0
5906 || RTStrVersionCompare(szRelease, "2.6.33") >= 0;
5907 if (fSendFileBetweenFiles)
5908 {
5909 /* Copy the whole file: */
5910 hFile1 = NIL_RTFILE;
5911 RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
5912 RTFileDelete(g_szDir2);
5913 hFile2 = NIL_RTFILE;
5914 RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
5915 ssize_t cbSent = sendfile((int)RTFileToNative(hFile2), (int)RTFileToNative(hFile1), NULL, cbFile);
5916 if (cbSent < 0)
5917 RTTestIFailed("sendfile(file,file,NULL,%#zx) failed (%zd): %d (%Rrc)",
5918 cbFile, cbSent, errno, RTErrConvertFromErrno(errno));
5919 else if ((size_t)cbSent != cbFileMax)
5920 RTTestIFailed("sendfile(file,file,NULL,%#zx) returned %#zx, expected %#zx (diff %zd)",
5921 cbFile, cbSent, cbFileMax, cbSent - cbFileMax);
5922 RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
5923 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
5924 RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
5925
5926 /* Try copy a little bit too much: */
5927 if (cbFile == cbFileMax)
5928 {
5929 hFile1 = NIL_RTFILE;
5930 RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
5931 RTFileDelete(g_szDir2);
5932 hFile2 = NIL_RTFILE;
5933 RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
5934 size_t cbToCopy = cbFile + RTRandU32Ex(1, _64M);
5935 cbSent = sendfile((int)RTFileToNative(hFile2), (int)RTFileToNative(hFile1), NULL, cbToCopy);
5936 if (cbSent < 0)
5937 RTTestIFailed("sendfile(file,file,NULL,%#zx) failed (%zd): %d (%Rrc)",
5938 cbToCopy, cbSent, errno, RTErrConvertFromErrno(errno));
5939 else if ((size_t)cbSent != cbFile)
5940 RTTestIFailed("sendfile(file,file,NULL,%#zx) returned %#zx, expected %#zx (diff %zd)",
5941 cbToCopy, cbSent, cbFile, cbSent - cbFile);
5942 RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
5943 RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
5944 }
5945
5946 /* Do partial copy: */
5947 hFile2 = NIL_RTFILE;
5948 RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
5949 for (uint32_t i = 0; i < 64; i++)
5950 {
5951 size_t cbToCopy = RTRandU32Ex(0, cbFileMax - 1);
5952 uint32_t const offFile = RTRandU32Ex(1, (uint64_t)RT_MIN(cbFileMax - cbToCopy, UINT32_MAX));
5953 RTTESTI_CHECK_RC_BREAK(RTFileSeek(hFile2, offFile, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
5954 loff_t offFile2 = offFile;
5955 cbSent = sendfile((int)RTFileToNative(hFile2), (int)RTFileToNative(hFile1), &offFile2, cbToCopy);
5956 if (cbSent < 0)
5957 RTTestIFailed("sendfile(file,file,%#x,%#zx) failed (%zd): %d (%Rrc)",
5958 offFile, cbToCopy, cbSent, errno, RTErrConvertFromErrno(errno));
5959 else if ((size_t)cbSent != cbToCopy)
5960 RTTestIFailed("sendfile(file,file,%#x,%#zx) returned %#zx, expected %#zx (diff %zd)",
5961 offFile, cbToCopy, cbSent, cbToCopy, cbSent - cbToCopy);
5962 else if (offFile2 != (loff_t)(offFile + cbToCopy))
5963 RTTestIFailed("sendfile(file,file,%#x,%#zx) returned %#zx + off=%#RX64, expected off %#x",
5964 offFile, cbToCopy, cbSent, offFile2, offFile + cbToCopy);
5965 }
5966 RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
5967 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
5968 RTTESTI_CHECK_RC(RTFileCompare(g_szDir, g_szDir2), VINF_SUCCESS);
5969 }
5970#endif
5971
5972 /*
5973 * Do some benchmarking.
5974 */
5975#define PROFILE_COPY_FN(a_szOperation, a_fnCall) \
5976 do \
5977 { \
5978 /* Estimate how many iterations we need to fill up the given timeslot: */ \
5979 fsPerfYield(); \
5980 uint64_t nsStart = RTTimeNanoTS(); \
5981 uint64_t ns; \
5982 do \
5983 ns = RTTimeNanoTS(); \
5984 while (ns == nsStart); \
5985 nsStart = ns; \
5986 \
5987 uint64_t iIteration = 0; \
5988 do \
5989 { \
5990 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
5991 iIteration++; \
5992 ns = RTTimeNanoTS() - nsStart; \
5993 } while (ns < RT_NS_10MS); \
5994 ns /= iIteration; \
5995 if (ns > g_nsPerNanoTSCall + 32) \
5996 ns -= g_nsPerNanoTSCall; \
5997 uint64_t cIterations = g_nsTestRun / ns; \
5998 if (cIterations < 2) \
5999 cIterations = 2; \
6000 else if (cIterations & 1) \
6001 cIterations++; \
6002 \
6003 /* Do the actual profiling: */ \
6004 iIteration = 0; \
6005 fsPerfYield(); \
6006 nsStart = RTTimeNanoTS(); \
6007 for (uint32_t iAdjust = 0; iAdjust < 4; iAdjust++) \
6008 { \
6009 for (; iIteration < cIterations; iIteration++)\
6010 RTTESTI_CHECK_RC(a_fnCall, VINF_SUCCESS); \
6011 ns = RTTimeNanoTS() - nsStart;\
6012 if (ns >= g_nsTestRun - (g_nsTestRun / 10)) \
6013 break; \
6014 cIterations += cIterations / 4; \
6015 if (cIterations & 1) \
6016 cIterations++; \
6017 nsStart += g_nsPerNanoTSCall; \
6018 } \
6019 RTTestIValueF(ns / iIteration, \
6020 RTTESTUNIT_NS_PER_OCCURRENCE, a_szOperation " latency"); \
6021 RTTestIValueF((uint64_t)((uint64_t)iIteration * cbFile / ((double)ns / RT_NS_1SEC)), \
6022 RTTESTUNIT_BYTES_PER_SEC, a_szOperation " throughput"); \
6023 RTTestIValueF((uint64_t)iIteration * cbFile, \
6024 RTTESTUNIT_BYTES, a_szOperation " bytes"); \
6025 RTTestIValueF(iIteration, \
6026 RTTESTUNIT_OCCURRENCES, a_szOperation " iterations"); \
6027 if (g_fShowDuration) \
6028 RTTestIValueF(ns, RTTESTUNIT_NS, a_szOperation " duration"); \
6029 } while (0)
6030
6031 PROFILE_COPY_FN("RTFileCopy/Replace", fsPerfCopyWorker1(g_szDir, g_szDir2));
6032
6033 hFile1 = NIL_RTFILE;
6034 RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
6035 RTFileDelete(g_szDir2);
6036 hFile2 = NIL_RTFILE;
6037 RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
6038 PROFILE_COPY_FN("RTFileCopyByHandles/Overwrite", RTFileCopyByHandles(hFile1, hFile2));
6039 RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
6040 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
6041
6042 /* We could benchmark RTFileCopyPart with various block sizes and whatnot...
6043 But it's currently well covered by the two previous operations. */
6044
6045#ifdef RT_OS_LINUX
6046 if (fSendFileBetweenFiles)
6047 {
6048 hFile1 = NIL_RTFILE;
6049 RTTESTI_CHECK_RC(RTFileOpen(&hFile1, g_szDir, RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_READ), VINF_SUCCESS);
6050 RTFileDelete(g_szDir2);
6051 hFile2 = NIL_RTFILE;
6052 RTTESTI_CHECK_RC(RTFileOpen(&hFile2, g_szDir2, RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE | RTFILE_O_WRITE), VINF_SUCCESS);
6053 PROFILE_COPY_FN("sendfile/overwrite", fsPerfCopyWorkerSendFile(hFile1, hFile2, cbFileMax));
6054 RTTESTI_CHECK_RC(RTFileClose(hFile2), VINF_SUCCESS);
6055 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
6056 }
6057#endif
6058 }
6059
6060 /*
6061 * Clean up.
6062 */
6063 RTFileDelete(InDir2(RT_STR_TUPLE("file22c1")));
6064 RTFileDelete(InDir2(RT_STR_TUPLE("file22c2")));
6065 RTFileDelete(InDir2(RT_STR_TUPLE("file22c3")));
6066 RTTESTI_CHECK_RC(RTFileDelete(g_szDir), VINF_SUCCESS);
6067}
6068
6069
6070static void fsPerfRemote(void)
6071{
6072 RTTestISub("remote");
6073 uint8_t abBuf[16384];
6074
6075
6076 /*
6077 * Create a file on the remote end and check that we can immediately see it.
6078 */
6079 RTTESTI_CHECK_RC_RETV(FsPerfCommsSend("reset\n"
6080 "open 0 'file30' 'w' 'ca'\n"
6081 "writepattern 0 0 0 4096" FSPERF_EOF_STR), VINF_SUCCESS);
6082
6083 RTFILEACTION enmActuallyTaken = RTFILEACTION_END;
6084 RTFILE hFile0 = NIL_RTFILE;
6085 RTTESTI_CHECK_RC(RTFileOpenEx(InDir(RT_STR_TUPLE("file30")), RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
6086 &hFile0, &enmActuallyTaken), VINF_SUCCESS);
6087 RTTESTI_CHECK(enmActuallyTaken == RTFILEACTION_OPENED);
6088 RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 4096, NULL), VINF_SUCCESS);
6089 AssertCompile(RT_ELEMENTS(g_abPattern0) == 1);
6090 RTTESTI_CHECK(ASMMemIsAllU8(abBuf, 4096, g_abPattern0[0]));
6091 RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
6092
6093 /*
6094 * Append a little to it on the host and see that we can read it.
6095 */
6096 RTTESTI_CHECK_RC(FsPerfCommsSend("writepattern 0 4096 1 1024" FSPERF_EOF_STR), VINF_SUCCESS);
6097 AssertCompile(RT_ELEMENTS(g_abPattern1) == 1);
6098 RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1024, NULL), VINF_SUCCESS);
6099 RTTESTI_CHECK(ASMMemIsAllU8(abBuf, 1024, g_abPattern1[0]));
6100 RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
6101
6102 /*
6103 * Have the host truncate the file.
6104 */
6105 RTTESTI_CHECK_RC(FsPerfCommsSend("truncate 0 1024" FSPERF_EOF_STR), VINF_SUCCESS);
6106 RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
6107 RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
6108 RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1024, NULL), VINF_SUCCESS);
6109 AssertCompile(RT_ELEMENTS(g_abPattern0) == 1);
6110 RTTESTI_CHECK(ASMMemIsAllU8(abBuf, 4096, g_abPattern0[0]));
6111 RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
6112
6113 /*
6114 * Write a bunch of stuff to the file here, then truncate it to a given size,
6115 * then have the host add more, finally test that we can successfully chop off
6116 * what the host added by reissuing the same truncate call as before (issue of
6117 * RDBSS using cached size to noop out set-eof-to-same-size).
6118 */
6119 memset(abBuf, 0xe9, sizeof(abBuf));
6120 RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
6121 RTTESTI_CHECK_RC(RTFileWrite(hFile0, abBuf, 16384, NULL), VINF_SUCCESS);
6122 RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 8000), VINF_SUCCESS);
6123 RTTESTI_CHECK_RC(FsPerfCommsSend("writepattern 0 8000 0 1000" FSPERF_EOF_STR), VINF_SUCCESS);
6124 RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 8000), VINF_SUCCESS);
6125 uint64_t cbFile = 0;
6126 RTTESTI_CHECK_RC(RTFileGetSize(hFile0, &cbFile), VINF_SUCCESS);
6127 RTTESTI_CHECK_MSG(cbFile == 8000, ("cbFile=%u\n", cbFile));
6128 RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
6129
6130 /* Same, but using RTFileRead to find out and RTFileWrite to define the size. */
6131 RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
6132 RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 0), VINF_SUCCESS);
6133 RTTESTI_CHECK_RC(RTFileWrite(hFile0, abBuf, 5000, NULL), VINF_SUCCESS);
6134 RTTESTI_CHECK_RC(FsPerfCommsSend("writepattern 0 5000 0 1000" FSPERF_EOF_STR), VINF_SUCCESS);
6135 RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 5000), VINF_SUCCESS);
6136 RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
6137 RTTESTI_CHECK_RC(RTFileGetSize(hFile0, &cbFile), VINF_SUCCESS);
6138 RTTESTI_CHECK_MSG(cbFile == 5000, ("cbFile=%u\n", cbFile));
6139
6140 /* Same, but host truncates rather than adding stuff. */
6141 RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
6142 RTTESTI_CHECK_RC(RTFileWrite(hFile0, abBuf, 16384, NULL), VINF_SUCCESS);
6143 RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 10000), VINF_SUCCESS);
6144 RTTESTI_CHECK_RC(FsPerfCommsSend("truncate 0 4000" FSPERF_EOF_STR), VINF_SUCCESS);
6145 RTTESTI_CHECK_RC(RTFileGetSize(hFile0, &cbFile), VINF_SUCCESS);
6146 RTTESTI_CHECK_MSG(cbFile == 4000, ("cbFile=%u\n", cbFile));
6147 RTTESTI_CHECK_RC(RTFileRead(hFile0, abBuf, 1, NULL), VERR_EOF);
6148
6149 /*
6150 * Test noticing remote size changes when opening a file. Need to keep hFile0
6151 * open here so we're sure to have an inode/FCB for the file in question.
6152 */
6153 memset(abBuf, 0xe7, sizeof(abBuf));
6154 RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
6155 RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 0), VINF_SUCCESS);
6156 RTTESTI_CHECK_RC(RTFileWrite(hFile0, abBuf, 12288, NULL), VINF_SUCCESS);
6157 RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 12288), VINF_SUCCESS);
6158
6159 RTTESTI_CHECK_RC(FsPerfCommsSend("writepattern 0 12288 2 4096" FSPERF_EOF_STR), VINF_SUCCESS);
6160
6161 enmActuallyTaken = RTFILEACTION_END;
6162 RTFILE hFile1 = NIL_RTFILE;
6163 RTTESTI_CHECK_RC(RTFileOpenEx(InDir(RT_STR_TUPLE("file30")), RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
6164 &hFile1, &enmActuallyTaken), VINF_SUCCESS);
6165 RTTESTI_CHECK(enmActuallyTaken == RTFILEACTION_OPENED);
6166 AssertCompile(sizeof(abBuf) >= 16384);
6167 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 16384, NULL), VINF_SUCCESS);
6168 RTTESTI_CHECK(ASMMemIsAllU8(abBuf, 12288, 0xe7));
6169 AssertCompile(RT_ELEMENTS(g_abPattern2) == 1);
6170 RTTESTI_CHECK(ASMMemIsAllU8(&abBuf[12288], 4096, g_abPattern2[0]));
6171 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 1, NULL), VERR_EOF);
6172 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
6173
6174 /* Same, but remote end truncates the file: */
6175 memset(abBuf, 0xe6, sizeof(abBuf));
6176 RTTESTI_CHECK_RC(RTFileSeek(hFile0, 0, RTFILE_SEEK_BEGIN, NULL), VINF_SUCCESS);
6177 RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 0), VINF_SUCCESS);
6178 RTTESTI_CHECK_RC(RTFileWrite(hFile0, abBuf, 12288, NULL), VINF_SUCCESS);
6179 RTTESTI_CHECK_RC(RTFileSetSize(hFile0, 12288), VINF_SUCCESS);
6180
6181 RTTESTI_CHECK_RC(FsPerfCommsSend("truncate 0 7500" FSPERF_EOF_STR), VINF_SUCCESS);
6182
6183 enmActuallyTaken = RTFILEACTION_END;
6184 hFile1 = NIL_RTFILE;
6185 RTTESTI_CHECK_RC(RTFileOpenEx(InDir(RT_STR_TUPLE("file30")), RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
6186 &hFile1, &enmActuallyTaken), VINF_SUCCESS);
6187 RTTESTI_CHECK(enmActuallyTaken == RTFILEACTION_OPENED);
6188 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 7500, NULL), VINF_SUCCESS);
6189 RTTESTI_CHECK(ASMMemIsAllU8(abBuf, 7500, 0xe6));
6190 RTTESTI_CHECK_RC(RTFileRead(hFile1, abBuf, 1, NULL), VERR_EOF);
6191 RTTESTI_CHECK_RC(RTFileClose(hFile1), VINF_SUCCESS);
6192
6193 RTTESTI_CHECK_RC(RTFileClose(hFile0), VINF_SUCCESS);
6194}
6195
6196
6197
6198/**
6199 * Display the usage to @a pStrm.
6200 */
6201static void Usage(PRTSTREAM pStrm)
6202{
6203 char szExec[FSPERF_MAX_PATH];
6204 RTStrmPrintf(pStrm, "usage: %s <-d <testdir>> [options]\n",
6205 RTPathFilename(RTProcGetExecutablePath(szExec, sizeof(szExec))));
6206 RTStrmPrintf(pStrm, "\n");
6207 RTStrmPrintf(pStrm, "options: \n");
6208
6209 for (unsigned i = 0; i < RT_ELEMENTS(g_aCmdOptions); i++)
6210 {
6211 char szHelp[80];
6212 const char *pszHelp;
6213 switch (g_aCmdOptions[i].iShort)
6214 {
6215 case 'd': pszHelp = "The directory to use for testing. default: CWD/fstestdir"; break;
6216 case 'r': pszHelp = "Don't abspath test dir (good for deep dirs). default: disabled"; break;
6217 case 'e': pszHelp = "Enables all tests. default: -e"; break;
6218 case 'z': pszHelp = "Disables all tests. default: -e"; break;
6219 case 's': pszHelp = "Set benchmark duration in seconds. default: 10 sec"; break;
6220 case 'm': pszHelp = "Set benchmark duration in milliseconds. default: 10000 ms"; break;
6221 case 'v': pszHelp = "More verbose execution."; break;
6222 case 'q': pszHelp = "Quiet execution."; break;
6223 case 'h': pszHelp = "Displays this help and exit"; break;
6224 case 'V': pszHelp = "Displays the program revision"; break;
6225 case kCmdOpt_ShowDuration: pszHelp = "Show duration of profile runs. default: --no-show-duration"; break;
6226 case kCmdOpt_NoShowDuration: pszHelp = "Hide duration of profile runs. default: --no-show-duration"; break;
6227 case kCmdOpt_ShowIterations: pszHelp = "Show iteration count for profile runs. default: --no-show-iterations"; break;
6228 case kCmdOpt_NoShowIterations: pszHelp = "Hide iteration count for profile runs. default: --no-show-iterations"; break;
6229 case kCmdOpt_ManyFiles: pszHelp = "Count of files in big test dir. default: --many-files 10000"; break;
6230 case kCmdOpt_NoManyFiles: pszHelp = "Skip big test dir with many files. default: --many-files 10000"; break;
6231 case kCmdOpt_ManyTreeFilesPerDir: pszHelp = "Count of files per directory in test tree. default: 640"; break;
6232 case kCmdOpt_ManyTreeSubdirsPerDir: pszHelp = "Count of subdirs per directory in test tree. default: 16"; break;
6233 case kCmdOpt_ManyTreeDepth: pszHelp = "Depth of test tree (not counting root). default: 1"; break;
6234 case kCmdOpt_IgnoreNoCache: pszHelp = "Ignore error wrt no-cache handle. default: --no-ignore-no-cache"; break;
6235 case kCmdOpt_NoIgnoreNoCache: pszHelp = "Do not ignore error wrt no-cache handle. default: --no-ignore-no-cache"; break;
6236 case kCmdOpt_IoFileSize: pszHelp = "Size of file used for I/O tests. default: 512 MB"; break;
6237 case kCmdOpt_SetBlockSize: pszHelp = "Sets single I/O block size (in bytes)."; break;
6238 case kCmdOpt_AddBlockSize: pszHelp = "Adds an I/O block size (in bytes)."; break;
6239 default:
6240 if (g_aCmdOptions[i].iShort >= kCmdOpt_First)
6241 {
6242 if (RTStrStartsWith(g_aCmdOptions[i].pszLong, "--no-"))
6243 RTStrPrintf(szHelp, sizeof(szHelp), "Disables the '%s' test.", g_aCmdOptions[i].pszLong + 5);
6244 else
6245 RTStrPrintf(szHelp, sizeof(szHelp), "Enables the '%s' test.", g_aCmdOptions[i].pszLong + 2);
6246 pszHelp = szHelp;
6247 }
6248 else
6249 pszHelp = "Option undocumented";
6250 break;
6251 }
6252 if ((unsigned)g_aCmdOptions[i].iShort < 127U)
6253 {
6254 char szOpt[64];
6255 RTStrPrintf(szOpt, sizeof(szOpt), "%s, -%c", g_aCmdOptions[i].pszLong, g_aCmdOptions[i].iShort);
6256 RTStrmPrintf(pStrm, " %-19s %s\n", szOpt, pszHelp);
6257 }
6258 else
6259 RTStrmPrintf(pStrm, " %-19s %s\n", g_aCmdOptions[i].pszLong, pszHelp);
6260 }
6261}
6262
6263
6264static uint32_t fsPerfCalcManyTreeFiles(void)
6265{
6266 uint32_t cDirs = 1;
6267 for (uint32_t i = 0, cDirsAtLevel = 1; i < g_cManyTreeDepth; i++)
6268 {
6269 cDirs += cDirsAtLevel * g_cManyTreeSubdirsPerDir;
6270 cDirsAtLevel *= g_cManyTreeSubdirsPerDir;
6271 }
6272 return g_cManyTreeFilesPerDir * cDirs;
6273}
6274
6275
6276int main(int argc, char *argv[])
6277{
6278 /*
6279 * Init IPRT and globals.
6280 */
6281 int rc = RTTestInitAndCreate("FsPerf", &g_hTest);
6282 if (rc)
6283 return rc;
6284 RTListInit(&g_ManyTreeHead);
6285
6286 /*
6287 * Default values.
6288 */
6289 char szDefaultDir[32];
6290 const char *pszDir = szDefaultDir;
6291 RTStrPrintf(szDefaultDir, sizeof(szDefaultDir), "fstestdir-%u" RTPATH_SLASH_STR, RTProcSelf());
6292
6293 bool fCommsSlave = false;
6294
6295 RTGETOPTUNION ValueUnion;
6296 RTGETOPTSTATE GetState;
6297 RTGetOptInit(&GetState, argc, argv, g_aCmdOptions, RT_ELEMENTS(g_aCmdOptions), 1, 0 /* fFlags */);
6298 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
6299 {
6300 switch (rc)
6301 {
6302 case 'c':
6303 if (!g_fRelativeDir)
6304 rc = RTPathAbs(ValueUnion.psz, g_szCommsDir, sizeof(g_szCommsDir) - 128);
6305 else
6306 rc = RTStrCopy(g_szCommsDir, sizeof(g_szCommsDir) - 128, ValueUnion.psz);
6307 if (RT_FAILURE(rc))
6308 {
6309 RTTestFailed(g_hTest, "%s(%s) failed: %Rrc\n", g_fRelativeDir ? "RTStrCopy" : "RTAbsPath", pszDir, rc);
6310 return RTTestSummaryAndDestroy(g_hTest);
6311 }
6312 RTPathEnsureTrailingSeparator(g_szCommsDir, sizeof(g_szCommsDir));
6313 g_cchCommsDir = strlen(g_szCommsDir);
6314
6315 rc = RTPathJoin(g_szCommsSubDir, sizeof(g_szCommsSubDir) - 128, g_szCommsDir, "comms" RTPATH_SLASH_STR);
6316 if (RT_FAILURE(rc))
6317 {
6318 RTTestFailed(g_hTest, "RTPathJoin(%s,,'comms/') failed: %Rrc\n", g_szCommsDir, rc);
6319 return RTTestSummaryAndDestroy(g_hTest);
6320 }
6321 g_cchCommsSubDir = strlen(g_szCommsSubDir);
6322 break;
6323
6324 case 'C':
6325 fCommsSlave = true;
6326 break;
6327
6328 case 'd':
6329 pszDir = ValueUnion.psz;
6330 break;
6331
6332 case 'r':
6333 g_fRelativeDir = true;
6334 break;
6335
6336 case 's':
6337 if (ValueUnion.u32 == 0)
6338 g_nsTestRun = RT_NS_1SEC_64 * 10;
6339 else
6340 g_nsTestRun = ValueUnion.u32 * RT_NS_1SEC_64;
6341 break;
6342
6343 case 'm':
6344 if (ValueUnion.u64 == 0)
6345 g_nsTestRun = RT_NS_1SEC_64 * 10;
6346 else
6347 g_nsTestRun = ValueUnion.u64 * RT_NS_1MS;
6348 break;
6349
6350 case 'e':
6351 g_fManyFiles = true;
6352 g_fOpen = true;
6353 g_fFStat = true;
6354#ifdef RT_OS_WINDOWS
6355 g_fNtQueryInfoFile = true;
6356 g_fNtQueryVolInfoFile = true;
6357#endif
6358 g_fFChMod = true;
6359 g_fFUtimes = true;
6360 g_fStat = true;
6361 g_fChMod = true;
6362 g_fUtimes = true;
6363 g_fRename = true;
6364 g_fDirOpen = true;
6365 g_fDirEnum = true;
6366 g_fMkRmDir = true;
6367 g_fStatVfs = true;
6368 g_fRm = true;
6369 g_fChSize = true;
6370 g_fReadTests = true;
6371 g_fReadPerf = true;
6372#ifdef FSPERF_TEST_SENDFILE
6373 g_fSendFile = true;
6374#endif
6375#ifdef RT_OS_LINUX
6376 g_fSplice = true;
6377#endif
6378 g_fWriteTests = true;
6379 g_fWritePerf = true;
6380 g_fSeek = true;
6381 g_fFSync = true;
6382 g_fMMap = true;
6383 g_fCopy = true;
6384 g_fRemote = true;
6385 break;
6386
6387 case 'z':
6388 g_fManyFiles = false;
6389 g_fOpen = false;
6390 g_fFStat = false;
6391#ifdef RT_OS_WINDOWS
6392 g_fNtQueryInfoFile = false;
6393 g_fNtQueryVolInfoFile = false;
6394#endif
6395 g_fFChMod = false;
6396 g_fFUtimes = false;
6397 g_fStat = false;
6398 g_fChMod = false;
6399 g_fUtimes = false;
6400 g_fRename = false;
6401 g_fDirOpen = false;
6402 g_fDirEnum = false;
6403 g_fMkRmDir = false;
6404 g_fStatVfs = false;
6405 g_fRm = false;
6406 g_fChSize = false;
6407 g_fReadTests = false;
6408 g_fReadPerf = false;
6409#ifdef FSPERF_TEST_SENDFILE
6410 g_fSendFile = false;
6411#endif
6412#ifdef RT_OS_LINUX
6413 g_fSplice = false;
6414#endif
6415 g_fWriteTests = false;
6416 g_fWritePerf = false;
6417 g_fSeek = false;
6418 g_fFSync = false;
6419 g_fMMap = false;
6420 g_fCopy = false;
6421 g_fRemote = false;
6422 break;
6423
6424#define CASE_OPT(a_Stem) \
6425 case RT_CONCAT(kCmdOpt_,a_Stem): RT_CONCAT(g_f,a_Stem) = true; break; \
6426 case RT_CONCAT(kCmdOpt_No,a_Stem): RT_CONCAT(g_f,a_Stem) = false; break
6427 CASE_OPT(Open);
6428 CASE_OPT(FStat);
6429#ifdef RT_OS_WINDOWS
6430 CASE_OPT(NtQueryInfoFile);
6431 CASE_OPT(NtQueryVolInfoFile);
6432#endif
6433 CASE_OPT(FChMod);
6434 CASE_OPT(FUtimes);
6435 CASE_OPT(Stat);
6436 CASE_OPT(ChMod);
6437 CASE_OPT(Utimes);
6438 CASE_OPT(Rename);
6439 CASE_OPT(DirOpen);
6440 CASE_OPT(DirEnum);
6441 CASE_OPT(MkRmDir);
6442 CASE_OPT(StatVfs);
6443 CASE_OPT(Rm);
6444 CASE_OPT(ChSize);
6445 CASE_OPT(ReadTests);
6446 CASE_OPT(ReadPerf);
6447#ifdef FSPERF_TEST_SENDFILE
6448 CASE_OPT(SendFile);
6449#endif
6450#ifdef RT_OS_LINUX
6451 CASE_OPT(Splice);
6452#endif
6453 CASE_OPT(WriteTests);
6454 CASE_OPT(WritePerf);
6455 CASE_OPT(Seek);
6456 CASE_OPT(FSync);
6457 CASE_OPT(MMap);
6458 CASE_OPT(IgnoreNoCache);
6459 CASE_OPT(Copy);
6460 CASE_OPT(Remote);
6461
6462 CASE_OPT(ShowDuration);
6463 CASE_OPT(ShowIterations);
6464#undef CASE_OPT
6465
6466 case kCmdOpt_ManyFiles:
6467 g_fManyFiles = ValueUnion.u32 > 0;
6468 g_cManyFiles = ValueUnion.u32;
6469 break;
6470
6471 case kCmdOpt_NoManyFiles:
6472 g_fManyFiles = false;
6473 break;
6474
6475 case kCmdOpt_ManyTreeFilesPerDir:
6476 if (ValueUnion.u32 > 0 && ValueUnion.u32 <= _64M)
6477 {
6478 g_cManyTreeFilesPerDir = ValueUnion.u32;
6479 g_cManyTreeFiles = fsPerfCalcManyTreeFiles();
6480 break;
6481 }
6482 RTTestFailed(g_hTest, "Out of range --files-per-dir value: %u (%#x)\n", ValueUnion.u32, ValueUnion.u32);
6483 return RTTestSummaryAndDestroy(g_hTest);
6484
6485 case kCmdOpt_ManyTreeSubdirsPerDir:
6486 if (ValueUnion.u32 > 0 && ValueUnion.u32 <= 1024)
6487 {
6488 g_cManyTreeSubdirsPerDir = ValueUnion.u32;
6489 g_cManyTreeFiles = fsPerfCalcManyTreeFiles();
6490 break;
6491 }
6492 RTTestFailed(g_hTest, "Out of range --subdirs-per-dir value: %u (%#x)\n", ValueUnion.u32, ValueUnion.u32);
6493 return RTTestSummaryAndDestroy(g_hTest);
6494
6495 case kCmdOpt_ManyTreeDepth:
6496 if (ValueUnion.u32 <= 8)
6497 {
6498 g_cManyTreeDepth = ValueUnion.u32;
6499 g_cManyTreeFiles = fsPerfCalcManyTreeFiles();
6500 break;
6501 }
6502 RTTestFailed(g_hTest, "Out of range --tree-depth value: %u (%#x)\n", ValueUnion.u32, ValueUnion.u32);
6503 return RTTestSummaryAndDestroy(g_hTest);
6504
6505 case kCmdOpt_IoFileSize:
6506 if (ValueUnion.u64 == 0)
6507 g_cbIoFile = _512M;
6508 else
6509 g_cbIoFile = ValueUnion.u64;
6510 break;
6511
6512 case kCmdOpt_SetBlockSize:
6513 if (ValueUnion.u32 > 0)
6514 {
6515 g_cIoBlocks = 1;
6516 g_acbIoBlocks[0] = ValueUnion.u32;
6517 }
6518 else
6519 {
6520 RTTestFailed(g_hTest, "Invalid I/O block size: %u (%#x)\n", ValueUnion.u32, ValueUnion.u32);
6521 return RTTestSummaryAndDestroy(g_hTest);
6522 }
6523 break;
6524
6525 case kCmdOpt_AddBlockSize:
6526 if (g_cIoBlocks >= RT_ELEMENTS(g_acbIoBlocks))
6527 RTTestFailed(g_hTest, "Too many I/O block sizes: max %u\n", RT_ELEMENTS(g_acbIoBlocks));
6528 else if (ValueUnion.u32 == 0)
6529 RTTestFailed(g_hTest, "Invalid I/O block size: %u (%#x)\n", ValueUnion.u32, ValueUnion.u32);
6530 else
6531 {
6532 g_acbIoBlocks[g_cIoBlocks++] = ValueUnion.u32;
6533 break;
6534 }
6535 return RTTestSummaryAndDestroy(g_hTest);
6536
6537 case 'q':
6538 g_uVerbosity = 0;
6539 break;
6540
6541 case 'v':
6542 g_uVerbosity++;
6543 break;
6544
6545 case 'h':
6546 Usage(g_pStdOut);
6547 return RTEXITCODE_SUCCESS;
6548
6549 case 'V':
6550 {
6551 char szRev[] = "$Revision: 78535 $";
6552 szRev[RT_ELEMENTS(szRev) - 2] = '\0';
6553 RTPrintf(RTStrStrip(strchr(szRev, ':') + 1));
6554 return RTEXITCODE_SUCCESS;
6555 }
6556
6557 default:
6558 return RTGetOptPrintError(rc, &ValueUnion);
6559 }
6560 }
6561
6562 /*
6563 * Populate g_szDir.
6564 */
6565 if (!g_fRelativeDir)
6566 rc = RTPathAbs(pszDir, g_szDir, sizeof(g_szDir) - FSPERF_MAX_NEEDED_PATH);
6567 else
6568 rc = RTStrCopy(g_szDir, sizeof(g_szDir) - FSPERF_MAX_NEEDED_PATH, pszDir);
6569 if (RT_FAILURE(rc))
6570 {
6571 RTTestFailed(g_hTest, "%s(%s) failed: %Rrc\n", g_fRelativeDir ? "RTStrCopy" : "RTAbsPath", pszDir, rc);
6572 return RTTestSummaryAndDestroy(g_hTest);
6573 }
6574 RTPathEnsureTrailingSeparator(g_szDir, sizeof(g_szDir));
6575 g_cchDir = strlen(g_szDir);
6576
6577 /*
6578 * If communication slave, go do that and be done.
6579 */
6580 if (fCommsSlave)
6581 {
6582 if (pszDir == szDefaultDir)
6583 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The slave must have a working directory specified (-d)!");
6584 return FsPerfCommsSlave();
6585 }
6586
6587 /*
6588 * Create the test directory with an 'empty' subdirectory under it,
6589 * execute the tests, and remove directory when done.
6590 */
6591 RTTestBanner(g_hTest);
6592 if (!RTPathExists(g_szDir))
6593 {
6594 /* The base dir: */
6595 rc = RTDirCreate(g_szDir, 0755,
6596 RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL);
6597 if (RT_SUCCESS(rc))
6598 {
6599 RTTestIPrintf(RTTESTLVL_ALWAYS, "Test dir: %s\n", g_szDir);
6600 rc = fsPrepTestArea();
6601 if (RT_SUCCESS(rc))
6602 {
6603 /* Profile RTTimeNanoTS(). */
6604 fsPerfNanoTS();
6605
6606 /* Do tests: */
6607 if (g_fManyFiles)
6608 fsPerfManyFiles();
6609 if (g_fOpen)
6610 fsPerfOpen();
6611 if (g_fFStat)
6612 fsPerfFStat();
6613#ifdef RT_OS_WINDOWS
6614 if (g_fNtQueryInfoFile)
6615 fsPerfNtQueryInfoFile();
6616 if (g_fNtQueryVolInfoFile)
6617 fsPerfNtQueryVolInfoFile();
6618#endif
6619 if (g_fFChMod)
6620 fsPerfFChMod();
6621 if (g_fFUtimes)
6622 fsPerfFUtimes();
6623 if (g_fStat)
6624 fsPerfStat();
6625 if (g_fChMod)
6626 fsPerfChmod();
6627 if (g_fUtimes)
6628 fsPerfUtimes();
6629 if (g_fRename)
6630 fsPerfRename();
6631 if (g_fDirOpen)
6632 vsPerfDirOpen();
6633 if (g_fDirEnum)
6634 vsPerfDirEnum();
6635 if (g_fMkRmDir)
6636 fsPerfMkRmDir();
6637 if (g_fStatVfs)
6638 fsPerfStatVfs();
6639 if (g_fRm || g_fManyFiles)
6640 fsPerfRm(); /* deletes manyfiles and manytree */
6641 if (g_fChSize)
6642 fsPerfChSize();
6643 if ( g_fReadPerf || g_fReadTests || g_fWritePerf || g_fWriteTests
6644#ifdef FSPERF_TEST_SENDFILE
6645 || g_fSendFile
6646#endif
6647#ifdef RT_OS_LINUX
6648 || g_fSplice
6649#endif
6650 || g_fSeek || g_fFSync || g_fMMap)
6651 fsPerfIo();
6652 if (g_fCopy)
6653 fsPerfCopy();
6654 if (g_fRemote && g_szCommsDir[0] != '\0')
6655 fsPerfRemote();
6656 }
6657
6658 /*
6659 * Cleanup:
6660 */
6661 FsPerfCommsShutdownSlave();
6662
6663 g_szDir[g_cchDir] = '\0';
6664 rc = RTDirRemoveRecursive(g_szDir, RTDIRRMREC_F_CONTENT_AND_DIR | (g_fRelativeDir ? RTDIRRMREC_F_NO_ABS_PATH : 0));
6665 if (RT_FAILURE(rc))
6666 RTTestFailed(g_hTest, "RTDirRemoveRecursive(%s,) -> %Rrc\n", g_szDir, rc);
6667 }
6668 else
6669 RTTestFailed(g_hTest, "RTDirCreate(%s) -> %Rrc\n", g_szDir, rc);
6670 }
6671 else
6672 RTTestFailed(g_hTest, "Test directory already exists: %s\n", g_szDir);
6673
6674 FsPerfCommsShutdownSlave();
6675
6676 return RTTestSummaryAndDestroy(g_hTest);
6677}
6678
Note: See TracBrowser for help on using the repository browser.

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