s3: Fix bug #9085.
[Samba.git] / source4 / torture / raw / lookuprate.c
blobe4caf7b1be27a88628628ac49df8118a405d4c9d
1 /*
2 File lookup rate test.
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/>.
20 #include "includes.h"
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)
37 struct rate_record
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. */
47 { 100, 0, 0},
48 { 1000, 0, 0},
49 { 10000, 0, 0},
50 { 100000, 0, 0}
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))) {
62 return true;
65 return false;
69 static NTSTATUS fill_directory(struct smbcli_tree *tree,
70 const char * path, unsigned count)
72 NTSTATUS status;
73 char *fname = NULL;
74 unsigned i;
75 unsigned current;
77 struct timeval start;
78 struct timeval now;
80 status = smbcli_mkdir(tree, path);
81 if (!NT_STATUS_IS_OK(status)) {
82 return status;
85 printf("filling directory %s with %u files... ", path, count);
86 fflush(stdout);
88 current = random();
89 start = timeval_current();
91 for (i = 0; i < count; ++i) {
92 int fnum;
94 ++current;
95 fname = talloc_asprintf(NULL, "%s\\fill%u",
96 path, current);
98 fnum = smbcli_open(tree, fname, O_RDONLY|O_CREAT,
99 OPENX_MODE_DENY_NONE);
100 if (fnum < 0) {
101 talloc_free(fname);
102 return smbcli_nt_error(tree);
105 smbcli_close(tree, fnum);
106 talloc_free(fname);
109 if (count) {
110 double rate;
111 now = timeval_current();
112 rate = (double)count / usec_to_sec((double)usec_time_diff(&now, &start));
113 printf("%u/sec\n", (unsigned)rate);
114 } else {
115 printf("done\n");
118 return NT_STATUS_OK;
121 static NTSTATUS squash_lookup_error(NTSTATUS status)
123 if (NT_STATUS_IS_OK(status)) {
124 return NT_STATUS_OK;
127 /* We don't care if the file isn't there. */
128 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
129 return NT_STATUS_OK;
132 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
133 return NT_STATUS_OK;
136 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
137 return NT_STATUS_OK;
140 return status;
143 /* Look up a pathname using TRANS2_QUERY_PATH_INFORMATION. */
144 static NTSTATUS querypath_lookup(struct smbcli_tree *tree, const char * path)
146 NTSTATUS status;
147 time_t ftimes[3];
148 size_t fsize;
149 uint16_t fmode;
151 status = smbcli_qpathinfo(tree, path, &ftimes[0], &ftimes[1], &ftimes[2],
152 &fsize, &fmode);
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)
172 NTSTATUS status;
174 struct timeval start;
175 struct timeval now;
176 unsigned count = 0;
177 int64_t elapsed = 0;
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)) {
186 return status;
189 ++count;
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));
197 return NT_STATUS_OK;
200 static bool remove_working_directory(struct smbcli_tree *tree,
201 const char * path)
203 int tries;
205 /* Using smbcli_deltree to delete a very large number of files
206 * doesn't work against all servers. Work around this by
207 * retrying.
209 for (tries = 0; tries < 5; ) {
210 int ret;
212 ret = smbcli_deltree(tree, BASEDIR);
213 if (ret == -1) {
214 tries++;
215 printf("(%s) failed to deltree %s: %s\n",
216 __location__, BASEDIR,
217 smbcli_errstr(tree));
218 continue;
221 return true;
224 return false;
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)
237 NTSTATUS status;
238 bool result = false;
240 int i;
241 struct smbcli_state *cli = NULL;
243 if (!torture_open_connection(&cli, torture, 0)) {
244 goto done;
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));
257 goto done;
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));
265 goto done;
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));
273 goto done;
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)) {
282 goto done;
286 /* Ok. We have run all our tests. Walk through the records we
287 * accumulated and figure out whether the lookups took constant
288 * time of not.
290 for (i = 0; i < ARRAY_SIZE(records); ++i) {
291 if (!fuzzily_equal(records[0].querypath_persec,
292 records[i].querypath_persec,
293 FUZZ_PERCENT)) {
294 printf("querypath rate for %d entries differed by "
295 "more than %d%% from base rate\n",
296 records[i].dirent_count, FUZZ_PERCENT);
297 result = false;
300 if (!fuzzily_equal(records[0].findfirst_persec,
301 records[i].findfirst_persec,
302 FUZZ_PERCENT)) {
303 printf("findfirst rate for %d entries differed by "
304 "more than %d%% from base rate\n",
305 records[i].dirent_count, FUZZ_PERCENT);
306 result = false;
310 done:
311 if (cli) {
312 remove_working_directory(cli->tree, BASEDIR);
313 talloc_free(cli);
316 return result;
319 /* vim: set sts=8 sw=8 : */