VirtualBox

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

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

IPRT/dir-poix.cpp,FsPerf.cpp: Darwin adjustments, realizing that the posix mkdir/rmdir code in IPRT does unnecessary work compared to windows wrt return codes in the already-exists-by-is-not-a-directory cases. Better have the functions return same statuses on all platforms where possibble. bugref:9172

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