diff --git a/ext/tlib/testsuite.c b/ext/tlib/testsuite.c
index ad5d1c73..586bb5a5 100644
--- a/ext/tlib/testsuite.c
+++ b/ext/tlib/testsuite.c
@@ -48,25 +48,51 @@ void tlib_add_test_opt(const char *name, void (*fn)(void))
add_test_to_list(name, fn, 1, &test_tail);
}
+struct hook
+{
+ void (*fn)(const char *name, void (*fn)(void), int v);
+ struct hook *next;
+};
+static struct hook hook_sentinel = { .next = &hook_sentinel };
+static struct hook *hook_tail = &hook_sentinel;
+static void add_hook_to_list(void (*fn)(void), struct hook **tail)
+{
+ struct hook *hook = calloc(1, sizeof *hook);
+ hook->fn = fn;
+ hook->next = (*tail)->next;
+ (*tail)->next = hook;
+ (*tail) = hook;
+}
+void tlib_add_hook(void (*fn)(const char *name, void (*fn)(void), int v))
+{
+ add_hook_to_list(fn, &hook_tail);
+}
+
static FILE *tlib_out, *tlib_err;
static jmp_buf test_jmp_buf, *test_jmp;
static char assert_buf[256];
static void test_printf(const char *fmt, ...);
static double run_test(struct test *test)
{
+ double res;
+ for (struct hook *hook = hook_tail->next->next; 0 != hook->fn; hook = hook->next)
+ hook->fn(test->name, test->fn, +1);
#if defined(_WIN64) || defined(_WIN32)
#pragma comment(lib, "winmm.lib")
unsigned long __stdcall timeGetTime(void);
unsigned long t0 = timeGetTime();
test->fn();
unsigned long t1 = timeGetTime();
- return (t1 - t0) / 1000.0;
+ res = (t1 - t0) / 1000.0;
#else
time_t t0 = time(0);
test->fn();
time_t t1 = time(0);
- return difftime(t1, t0);
+ res = difftime(t1, t0);
#endif
+ for (struct hook *hook = hook_tail->next->next; 0 != hook->fn; hook = hook->next)
+ hook->fn(test->name, test->fn, -1);
+ return res;
}
static void do_test_default(struct test *test, int testno)
{
diff --git a/ext/tlib/testsuite.h b/ext/tlib/testsuite.h
index a0c60654..84150ce4 100644
--- a/ext/tlib/testsuite.h
+++ b/ext/tlib/testsuite.h
@@ -65,6 +65,23 @@ void tlib_add_test_suite(const char *name, void (*fn)(void));
void tlib_add_test(const char *name, void (*fn)(void));
void tlib_add_test_opt(const char *name, void (*fn)(void));
+/**
+ * Register a test hook to be run before and after every test.
+ *
+ * Test hooks are functions with prototype
+ * void testhook(const char *name, void (*fn)(void), int v)
.
+ * The parameter v specifies that a test is about to be executed (v is +1)
+ * or it was just executed (v is -1).
+ */
+#define TESTHOOK(fn)\
+ do\
+ {\
+ void fn(const char *name, void (*fn)(void), int v);\
+ tlib_add_hook(fn);\
+ } while (0)
+
+void tlib_add_hook(void (*fn)(const char *name, void (*fn)(void), int v));
+
/**
* Printf function.
*
diff --git a/tools/run-perf-tests.bat b/tools/run-perf-tests.bat
index fc7ca2e7..0c3409b0 100755
--- a/tools/run-perf-tests.bat
+++ b/tools/run-perf-tests.bat
@@ -30,25 +30,25 @@ pushd fsbench
set OptFiles=1000 2000 3000 4000 5000
if X%2==Xbaseline set OptFiles=10000
for %%a in (%OptFiles%) do (
- call :csv %%a "%fsbench% --files=%%a file_*"
+ call :csv %%a "%fsbench% --empty-cache --files=%%a file_*"
)
set OptRdwrCc=100 200 300 400 500
if X%2==Xbaseline set OptRdwrCc=1000
for %%a in (%OptRdwrCc%) do (
- call :csv %%a "%fsbench% --rdwr-cc=%%a rdwr_cc_*"
+ call :csv %%a "%fsbench% --empty-cache --rdwr-cc=%%a rdwr_cc_*"
)
set OptRdwrNc=100 200 300 400 500
if X%2==Xbaseline set OptRdwrNc=100
for %%a in (%OptRdwrNc%) do (
- call :csv %%a "%fsbench% --rdwr-nc=%%a rdwr_nc_*"
+ call :csv %%a "%fsbench% --empty-cache --rdwr-nc=%%a rdwr_nc_*"
)
set OptMmap=100 200 300 400 500
if X%2==Xbaseline set OptMmap=1000
for %%a in (%OptMmap%) do (
- call :csv %%a "%fsbench% --mmap=%%a mmap_*"
+ call :csv %%a "%fsbench% --empty-cache --mmap=%%a mmap_*"
)
popd
diff --git a/tst/fsbench/fsbench.c b/tst/fsbench/fsbench.c
index 229b80d7..1ac04b12 100644
--- a/tst/fsbench/fsbench.c
+++ b/tst/fsbench/fsbench.c
@@ -23,6 +23,7 @@
#include
#include
+static BOOLEAN OptEmptyCache = FALSE;
static ULONG OptFileCount = 1000;
static ULONG OptListCount = 100;
static ULONG OptRdwrFileSize = 4096 * 1024;
@@ -398,6 +399,64 @@ static void mmap_tests(void)
TEST(mmap_read_test);
}
+static void EmptyCache(const char *name, void (*fn)(void), int v)
+{
+ NTSYSCALLAPI NTSTATUS NTAPI
+ NtSetSystemInformation(
+ ULONG SystemInformationClass,
+ PVOID SystemInformation,
+ ULONG SystemInformationLength);
+
+ if (+1 == v) /* setup */
+ {
+ /* flush the file system cache (requires SE_INCREASE_QUOTA_NAME) */
+ ASSERT(SetSystemFileCacheSize((SIZE_T)-1, (SIZE_T)-1, 0));
+
+ /* flush/purge the standby list (requires SE_PROF_SINGLE_PROCESS_NAME) */
+ ULONG Command;
+ Command = 3 /*MemoryFlushModifiedList*/;
+ ASSERT(0 == NtSetSystemInformation(80 /*SystemMemoryListInformation*/, &Command, sizeof Command));
+ Command = 4 /*MemoryPurgeStandbyList*/;
+ ASSERT(0 == NtSetSystemInformation(80 /*SystemMemoryListInformation*/, &Command, sizeof Command));
+ }
+ else
+ if (-1 == v) /* teardown */
+ {
+ }
+}
+static DWORD EnablePrivilegesForEmptyCache(VOID)
+{
+ union
+ {
+ TOKEN_PRIVILEGES P;
+ UINT8 B[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
+ } Privileges;
+ HANDLE Token;
+
+ Privileges.P.PrivilegeCount = 2;
+ Privileges.P.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ Privileges.P.Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
+
+ if (!LookupPrivilegeValueW(0, SE_PROF_SINGLE_PROCESS_NAME, &Privileges.P.Privileges[0].Luid) ||
+ !LookupPrivilegeValueW(0, SE_INCREASE_QUOTA_NAME, &Privileges.P.Privileges[1].Luid))
+ return GetLastError();
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &Token))
+ return GetLastError();
+
+ if (!AdjustTokenPrivileges(Token, FALSE, &Privileges.P, 0, 0, 0) ||
+ ERROR_NOT_ALL_ASSIGNED == GetLastError())
+ {
+ CloseHandle(Token);
+
+ return GetLastError();
+ }
+
+ CloseHandle(Token);
+
+ return ERROR_SUCCESS;
+}
+
#define rmarg(argv, argc, argi) \
argc--, \
memmove(argv + argi, argv + argi + 1, (argc - argi) * sizeof(char *)),\
@@ -414,7 +473,12 @@ int main(int argc, char *argv[])
const char *a = argv[argi];
if ('-' == a[0])
{
- if (0 == strncmp("--files=", a, sizeof "--files=" - 1))
+ if (0 == strcmp("--empty-cache", a))
+ {
+ OptEmptyCache = TRUE;
+ rmarg(argv, argc, argi);
+ }
+ else if (0 == strncmp("--files=", a, sizeof "--files=" - 1))
{
OptFileCount = strtoul(a + sizeof "--files=" - 1, 0, 10);
rmarg(argv, argc, argi);
@@ -442,6 +506,16 @@ int main(int argc, char *argv[])
}
}
+ if (OptEmptyCache)
+ {
+ if (0 != EnablePrivilegesForEmptyCache())
+ {
+ tlib_printf("ABORT: cannot enable privileges required for empty cache\n");
+ abort();
+ }
+ TESTHOOK(EmptyCache);
+ }
+
tlib_run_tests(argc, argv);
return 0;
}