4
Copyright (C) James Peach 2006
6
This program is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 3 of the License, or
9
(at your option) any later version.
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with this program. If not, see <http://www.gnu.org/licenses/>.
21
#include "system/filesys.h"
22
#include "torture/smbtorture.h"
23
#include "torture/basic/proto.h"
24
#include "libcli/libcli.h"
25
#include "torture/util.h"
26
#include "lib/cmdline/popt_common.h"
27
#include "auth/credentials/credentials.h"
29
#define BASEDIR "\\lookuprate"
30
#define MISSINGNAME BASEDIR "\\foo"
32
#define FUZZ_PERCENT 10
34
#define usec_to_sec(s) ((s) / 1000000)
35
#define sec_to_usec(s) ((s) * 1000000)
39
unsigned dirent_count;
40
unsigned querypath_persec;
41
unsigned findfirst_persec;
44
static struct rate_record records[] =
46
{ 0, 0, 0 }, /* Base (optimal) lookup rate. */
53
typedef NTSTATUS lookup_function(struct smbcli_tree *tree, const char * path);
55
/* Test whether rhs is within fuzz% of lhs. */
56
static bool fuzzily_equal(unsigned lhs, unsigned rhs, int percent)
58
double fuzz = (double)lhs * (double)percent/100.0;
60
if (((double)rhs >= ((double)lhs - fuzz)) &&
61
((double)rhs <= ((double)lhs + fuzz))) {
69
static NTSTATUS fill_directory(struct smbcli_tree *tree,
70
const char * path, unsigned count)
80
status = smbcli_mkdir(tree, path);
81
if (!NT_STATUS_IS_OK(status)) {
85
printf("filling directory %s with %u files... ", path, count);
89
start = timeval_current();
91
for (i = 0; i < count; ++i) {
95
fname = talloc_asprintf(NULL, "%s\\fill%u",
98
fnum = smbcli_open(tree, fname, O_RDONLY|O_CREAT,
99
OPENX_MODE_DENY_NONE);
102
return smbcli_nt_error(tree);
105
smbcli_close(tree, fnum);
111
now = timeval_current();
112
rate = (double)count / usec_to_sec((double)usec_time_diff(&now, &start));
113
printf("%u/sec\n", (unsigned)rate);
121
static NTSTATUS squash_lookup_error(NTSTATUS status)
123
if (NT_STATUS_IS_OK(status)) {
127
/* We don't care if the file isn't there. */
128
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
132
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
136
if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
143
/* Look up a pathname using TRANS2_QUERY_PATH_INFORMATION. */
144
static NTSTATUS querypath_lookup(struct smbcli_tree *tree, const char * path)
151
status = smbcli_qpathinfo(tree, path, &ftimes[0], &ftimes[1], &ftimes[2],
154
return squash_lookup_error(status);
157
/* Look up a pathname using TRANS2_FIND_FIRST2. */
158
static NTSTATUS findfirst_lookup(struct smbcli_tree *tree, const char * path)
160
NTSTATUS status = NT_STATUS_OK;
162
if (smbcli_list(tree, path, 0, NULL, NULL) < 0) {
163
status = smbcli_nt_error(tree);
166
return squash_lookup_error(status);
169
static NTSTATUS lookup_rate_convert(struct smbcli_tree *tree,
170
lookup_function lookup, const char * path, unsigned * rate)
174
struct timeval start;
179
#define LOOKUP_PERIOD_SEC (2)
181
start = timeval_current();
182
while (elapsed < sec_to_usec(LOOKUP_PERIOD_SEC)) {
184
status = lookup(tree, path);
185
if (!NT_STATUS_IS_OK(status)) {
190
now = timeval_current();
191
elapsed = usec_time_diff(&now, &start);
194
#undef LOOKUP_PERIOD_SEC
196
*rate = (unsigned)((double)count / (double)usec_to_sec(elapsed));
200
static bool remove_working_directory(struct smbcli_tree *tree,
205
/* Using smbcli_deltree to delete a very large number of files
206
* doesn't work against all servers. Work around this by
209
for (tries = 0; tries < 5; ) {
212
ret = smbcli_deltree(tree, BASEDIR);
215
printf("(%s) failed to deltree %s: %s\n",
216
__location__, BASEDIR,
217
smbcli_errstr(tree));
228
/* Verify that looking up a file name takes constant time.
230
* This test samples the lookup rate for a non-existant filename in a
231
* directory, while varying the number of files in the directory. The
232
* lookup rate should continue to approximate the lookup rate for the
233
* empty directory case.
235
bool torture_bench_lookup(struct torture_context *torture)
241
struct smbcli_state *cli = NULL;
243
if (!torture_open_connection(&cli, torture, 0)) {
247
remove_working_directory(cli->tree, BASEDIR);
249
for (i = 0; i < ARRAY_SIZE(records); ++i) {
250
printf("testing lookup rate with %u directory entries\n",
251
records[i].dirent_count);
253
status = fill_directory(cli->tree, BASEDIR,
254
records[i].dirent_count);
255
if (!NT_STATUS_IS_OK(status)) {
256
printf("failed to fill directory: %s\n", nt_errstr(status));
260
status = lookup_rate_convert(cli->tree, querypath_lookup,
261
MISSINGNAME, &records[i].querypath_persec);
262
if (!NT_STATUS_IS_OK(status)) {
263
printf("querypathinfo of %s failed: %s\n",
264
MISSINGNAME, nt_errstr(status));
268
status = lookup_rate_convert(cli->tree, findfirst_lookup,
269
MISSINGNAME, &records[i].findfirst_persec);
270
if (!NT_STATUS_IS_OK(status)) {
271
printf("findfirst of %s failed: %s\n",
272
MISSINGNAME, nt_errstr(status));
276
printf("entries = %u, querypath = %u/sec, findfirst = %u/sec\n",
277
records[i].dirent_count,
278
records[i].querypath_persec,
279
records[i].findfirst_persec);
281
if (!remove_working_directory(cli->tree, BASEDIR)) {
286
/* Ok. We have run all our tests. Walk through the records we
287
* accumulated and figure out whether the lookups took constant
290
for (i = 0; i < ARRAY_SIZE(records); ++i) {
291
if (!fuzzily_equal(records[0].querypath_persec,
292
records[i].querypath_persec,
294
printf("querypath rate for %d entries differed by "
295
"more than %d%% from base rate\n",
296
records[i].dirent_count, FUZZ_PERCENT);
300
if (!fuzzily_equal(records[0].findfirst_persec,
301
records[i].findfirst_persec,
303
printf("findfirst rate for %d entries differed by "
304
"more than %d%% from base rate\n",
305
records[i].dirent_count, FUZZ_PERCENT);
312
remove_working_directory(cli->tree, BASEDIR);
319
/* vim: set sts=8 sw=8 : */