Backport FSCTL codes from master
[Samba.git] / source4 / torture / raw / lookuprate.c
blob96ae92f1e87637beaaa260880e89209fde5e2ffd
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 "libcli/libcli.h"
24 #include "torture/util.h"
26 #define BASEDIR "\\lookuprate"
27 #define MISSINGNAME BASEDIR "\\foo"
29 #define FUZZ_PERCENT 10
31 #define usec_to_sec(s) ((s) / 1000000)
32 #define sec_to_usec(s) ((s) * 1000000)
34 struct rate_record
36 unsigned dirent_count;
37 unsigned querypath_persec;
38 unsigned findfirst_persec;
41 static struct rate_record records[] =
43 { 0, 0, 0 }, /* Base (optimal) lookup rate. */
44 { 100, 0, 0},
45 { 1000, 0, 0},
46 { 10000, 0, 0},
47 { 100000, 0, 0}
50 typedef NTSTATUS lookup_function(struct smbcli_tree *tree, const char * path);
52 /* Test whether rhs is within fuzz% of lhs. */
53 static bool fuzzily_equal(unsigned lhs, unsigned rhs, int percent)
55 double fuzz = (double)lhs * (double)percent/100.0;
57 if (((double)rhs >= ((double)lhs - fuzz)) &&
58 ((double)rhs <= ((double)lhs + fuzz))) {
59 return true;
62 return false;
66 static NTSTATUS fill_directory(struct smbcli_tree *tree,
67 const char * path, unsigned count)
69 NTSTATUS status;
70 char *fname = NULL;
71 unsigned i;
72 unsigned current;
74 struct timeval start;
75 struct timeval now;
77 status = smbcli_mkdir(tree, path);
78 if (!NT_STATUS_IS_OK(status)) {
79 return status;
82 printf("filling directory %s with %u files... ", path, count);
83 fflush(stdout);
85 current = random();
86 start = timeval_current();
88 for (i = 0; i < count; ++i) {
89 int fnum;
91 ++current;
92 fname = talloc_asprintf(NULL, "%s\\fill%u",
93 path, current);
95 fnum = smbcli_open(tree, fname, O_RDONLY|O_CREAT,
96 DENY_NONE);
97 if (fnum < 0) {
98 talloc_free(fname);
99 return smbcli_nt_error(tree);
102 smbcli_close(tree, fnum);
103 talloc_free(fname);
106 if (count) {
107 double rate;
108 now = timeval_current();
109 rate = (double)count / usec_to_sec((double)usec_time_diff(&now, &start));
110 printf("%u/sec\n", (unsigned)rate);
111 } else {
112 printf("done\n");
115 return NT_STATUS_OK;
118 static NTSTATUS squash_lookup_error(NTSTATUS status)
120 if (NT_STATUS_IS_OK(status)) {
121 return NT_STATUS_OK;
124 /* We don't care if the file isn't there. */
125 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
126 return NT_STATUS_OK;
129 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
130 return NT_STATUS_OK;
133 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
134 return NT_STATUS_OK;
137 return status;
140 /* Look up a pathname using TRANS2_QUERY_PATH_INFORMATION. */
141 static NTSTATUS querypath_lookup(struct smbcli_tree *tree, const char * path)
143 NTSTATUS status;
144 time_t ftimes[3];
145 size_t fsize;
146 uint16_t fmode;
148 status = smbcli_qpathinfo(tree, path, &ftimes[0], &ftimes[1], &ftimes[2],
149 &fsize, &fmode);
151 return squash_lookup_error(status);
154 /* Look up a pathname using TRANS2_FIND_FIRST2. */
155 static NTSTATUS findfirst_lookup(struct smbcli_tree *tree, const char * path)
157 NTSTATUS status = NT_STATUS_OK;
159 if (smbcli_list(tree, path, 0, NULL, NULL) < 0) {
160 status = smbcli_nt_error(tree);
163 return squash_lookup_error(status);
166 static NTSTATUS lookup_rate_convert(struct smbcli_tree *tree,
167 lookup_function lookup, const char * path, unsigned * rate)
169 NTSTATUS status;
171 struct timeval start;
172 struct timeval now;
173 unsigned count = 0;
174 int64_t elapsed = 0;
176 #define LOOKUP_PERIOD_SEC (2)
178 start = timeval_current();
179 while (elapsed < sec_to_usec(LOOKUP_PERIOD_SEC)) {
181 status = lookup(tree, path);
182 if (!NT_STATUS_IS_OK(status)) {
183 return status;
186 ++count;
187 now = timeval_current();
188 elapsed = usec_time_diff(&now, &start);
191 #undef LOOKUP_PERIOD_SEC
193 *rate = (unsigned)((double)count / (double)usec_to_sec(elapsed));
194 return NT_STATUS_OK;
197 static bool remove_working_directory(struct smbcli_tree *tree,
198 const char * path)
200 int tries;
202 /* Using smbcli_deltree to delete a very large number of files
203 * doesn't work against all servers. Work around this by
204 * retrying.
206 for (tries = 0; tries < 5; ) {
207 int ret;
209 ret = smbcli_deltree(tree, BASEDIR);
210 if (ret == -1) {
211 tries++;
212 printf("(%s) failed to deltree %s: %s\n",
213 __location__, BASEDIR,
214 smbcli_errstr(tree));
215 continue;
218 return true;
221 return false;
225 /* Verify that looking up a file name takes constant time.
227 * This test samples the lookup rate for a non-existant filename in a
228 * directory, while varying the number of files in the directory. The
229 * lookup rate should continue to approximate the lookup rate for the
230 * empty directory case.
232 bool torture_bench_lookup(struct torture_context *torture)
234 NTSTATUS status;
235 bool result = false;
237 int i;
238 struct smbcli_state *cli = NULL;
240 if (!torture_open_connection(&cli, torture, 0)) {
241 goto done;
244 remove_working_directory(cli->tree, BASEDIR);
246 for (i = 0; i < ARRAY_SIZE(records); ++i) {
247 printf("Testing lookup rate with %u directory entries\n",
248 records[i].dirent_count);
250 status = fill_directory(cli->tree, BASEDIR,
251 records[i].dirent_count);
252 if (!NT_STATUS_IS_OK(status)) {
253 printf("failed to fill directory: %s\n", nt_errstr(status));
254 goto done;
257 status = lookup_rate_convert(cli->tree, querypath_lookup,
258 MISSINGNAME, &records[i].querypath_persec);
259 if (!NT_STATUS_IS_OK(status)) {
260 printf("querypathinfo of %s failed: %s\n",
261 MISSINGNAME, nt_errstr(status));
262 goto done;
265 status = lookup_rate_convert(cli->tree, findfirst_lookup,
266 MISSINGNAME, &records[i].findfirst_persec);
267 if (!NT_STATUS_IS_OK(status)) {
268 printf("findfirst of %s failed: %s\n",
269 MISSINGNAME, nt_errstr(status));
270 goto done;
273 printf("entries = %u, querypath = %u/sec, findfirst = %u/sec\n",
274 records[i].dirent_count,
275 records[i].querypath_persec,
276 records[i].findfirst_persec);
278 if (!remove_working_directory(cli->tree, BASEDIR)) {
279 goto done;
283 /* Ok. We have run all our tests. Walk through the records we
284 * accumulated and figure out whether the lookups took constant
285 * time of not.
287 for (i = 0; i < ARRAY_SIZE(records); ++i) {
288 if (!fuzzily_equal(records[0].querypath_persec,
289 records[i].querypath_persec,
290 FUZZ_PERCENT)) {
291 printf("querypath rate for %d entries differed by "
292 "more than %d%% from base rate\n",
293 records[i].dirent_count, FUZZ_PERCENT);
294 result = false;
297 if (!fuzzily_equal(records[0].findfirst_persec,
298 records[i].findfirst_persec,
299 FUZZ_PERCENT)) {
300 printf("findfirst rate for %d entries differed by "
301 "more than %d%% from base rate\n",
302 records[i].dirent_count, FUZZ_PERCENT);
303 result = false;
307 done:
308 if (cli) {
309 remove_working_directory(cli->tree, BASEDIR);
310 talloc_free(cli);
313 return result;
316 /* vim: set sts=8 sw=8 : */