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 "param/param.h"
22 #include "system/filesys.h"
23 #include "torture/torture.h"
24 #include "torture/basic/proto.h"
25 #include "libcli/libcli.h"
26 #include "torture/util.h"
27 #include "lib/cmdline/popt_common.h"
28 #include "auth/credentials/credentials.h"
30 #define BASEDIR "\\lookuprate"
31 #define MISSINGNAME BASEDIR "\\foo"
33 #define FUZZ_PERCENT 10
35 #define usec_to_sec(s) ((s) / 1000000)
36 #define sec_to_usec(s) ((s) * 1000000)
40 unsigned dirent_count
;
41 unsigned querypath_persec
;
42 unsigned findfirst_persec
;
45 static struct rate_record records
[] =
47 { 0, 0, 0 }, /* Base (optimal) lookup rate. */
54 typedef NTSTATUS
lookup_function(struct smbcli_tree
*tree
, const char * path
);
56 /* Test whether rhs is within fuzz% of lhs. */
57 static bool fuzzily_equal(unsigned lhs
, unsigned rhs
, int percent
)
59 double fuzz
= (double)lhs
* (double)percent
/100.0;
61 if (((double)rhs
>= ((double)lhs
- fuzz
)) &&
62 ((double)rhs
<= ((double)lhs
+ fuzz
))) {
70 static NTSTATUS
fill_directory(struct smbcli_tree
*tree
,
71 const char * path
, unsigned count
)
81 status
= smbcli_mkdir(tree
, path
);
82 if (!NT_STATUS_IS_OK(status
)) {
86 printf("filling directory %s with %u files... ", path
, count
);
90 start
= timeval_current();
92 for (i
= 0; i
< count
; ++i
) {
96 fname
= talloc_asprintf(NULL
, "%s\\fill%u",
99 fnum
= smbcli_open(tree
, fname
, O_RDONLY
|O_CREAT
,
100 OPENX_MODE_DENY_NONE
);
103 return smbcli_nt_error(tree
);
106 smbcli_close(tree
, fnum
);
112 now
= timeval_current();
113 rate
= (double)count
/ usec_to_sec((double)usec_time_diff(&now
, &start
));
114 printf("%u/sec\n", (unsigned)rate
);
122 static NTSTATUS
squash_lookup_error(NTSTATUS status
)
124 if (NT_STATUS_IS_OK(status
)) {
128 /* We don't care if the file isn't there. */
129 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_PATH_NOT_FOUND
)) {
133 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
137 if (NT_STATUS_EQUAL(status
, NT_STATUS_NO_SUCH_FILE
)) {
144 /* Look up a pathname using TRANS2_QUERY_PATH_INFORMATION. */
145 static NTSTATUS
querypath_lookup(struct smbcli_tree
*tree
, const char * path
)
152 status
= smbcli_qpathinfo(tree
, path
, &ftimes
[0], &ftimes
[1], &ftimes
[2],
155 return squash_lookup_error(status
);
158 /* Look up a pathname using TRANS2_FIND_FIRST2. */
159 static NTSTATUS
findfirst_lookup(struct smbcli_tree
*tree
, const char * path
)
161 NTSTATUS status
= NT_STATUS_OK
;
163 if (smbcli_list(tree
, path
, 0, NULL
, NULL
) < 0) {
164 status
= smbcli_nt_error(tree
);
167 return squash_lookup_error(status
);
170 static NTSTATUS
lookup_rate_convert(struct smbcli_tree
*tree
,
171 lookup_function lookup
, const char * path
, unsigned * rate
)
175 struct timeval start
;
180 #define LOOKUP_PERIOD_SEC (2)
182 start
= timeval_current();
183 while (elapsed
< sec_to_usec(LOOKUP_PERIOD_SEC
)) {
185 status
= lookup(tree
, path
);
186 if (!NT_STATUS_IS_OK(status
)) {
191 now
= timeval_current();
192 elapsed
= usec_time_diff(&now
, &start
);
195 #undef LOOKUP_PERIOD_SEC
197 *rate
= (unsigned)((double)count
/ (double)usec_to_sec(elapsed
));
201 static bool remove_working_directory(struct smbcli_tree
*tree
,
206 /* Using smbcli_deltree to delete a very large number of files
207 * doesn't work against all servers. Work around this by
210 for (tries
= 0; tries
< 5; ) {
213 ret
= smbcli_deltree(tree
, BASEDIR
);
216 printf("(%s) failed to deltree %s: %s\n",
217 __location__
, BASEDIR
,
218 smbcli_errstr(tree
));
229 /* Verify that looking up a file name takes constant time.
231 * This test samples the lookup rate for a non-existant filename in a
232 * directory, while varying the number of files in the directory. The
233 * lookup rate should continue to approximate the lookup rate for the
234 * empty directory case.
236 bool torture_bench_lookup(struct torture_context
*torture
)
242 struct smbcli_state
*cli
= NULL
;
244 if (!torture_open_connection(&cli
, torture
, 0)) {
248 remove_working_directory(cli
->tree
, BASEDIR
);
250 for (i
= 0; i
< ARRAY_SIZE(records
); ++i
) {
251 printf("testing lookup rate with %u directory entries\n",
252 records
[i
].dirent_count
);
254 status
= fill_directory(cli
->tree
, BASEDIR
,
255 records
[i
].dirent_count
);
256 if (!NT_STATUS_IS_OK(status
)) {
257 printf("failed to fill directory: %s\n", nt_errstr(status
));
261 status
= lookup_rate_convert(cli
->tree
, querypath_lookup
,
262 MISSINGNAME
, &records
[i
].querypath_persec
);
263 if (!NT_STATUS_IS_OK(status
)) {
264 printf("querypathinfo of %s failed: %s\n",
265 MISSINGNAME
, nt_errstr(status
));
269 status
= lookup_rate_convert(cli
->tree
, findfirst_lookup
,
270 MISSINGNAME
, &records
[i
].findfirst_persec
);
271 if (!NT_STATUS_IS_OK(status
)) {
272 printf("findfirst of %s failed: %s\n",
273 MISSINGNAME
, nt_errstr(status
));
277 printf("entries = %u, querypath = %u/sec, findfirst = %u/sec\n",
278 records
[i
].dirent_count
,
279 records
[i
].querypath_persec
,
280 records
[i
].findfirst_persec
);
282 if (!remove_working_directory(cli
->tree
, BASEDIR
)) {
287 /* Ok. We have run all our tests. Walk through the records we
288 * accumulated and figure out whether the lookups took constant
291 for (i
= 0; i
< ARRAY_SIZE(records
); ++i
) {
292 if (!fuzzily_equal(records
[0].querypath_persec
,
293 records
[i
].querypath_persec
,
295 printf("querypath rate for %d entries differed by "
296 "more than %d%% from base rate\n",
297 records
[i
].dirent_count
, FUZZ_PERCENT
);
301 if (!fuzzily_equal(records
[0].findfirst_persec
,
302 records
[i
].findfirst_persec
,
304 printf("findfirst rate for %d entries differed by "
305 "more than %d%% from base rate\n",
306 records
[i
].dirent_count
, FUZZ_PERCENT
);
313 remove_working_directory(cli
->tree
, BASEDIR
);
320 /* vim: set sts=8 sw=8 : */