Fix build
[Samba/ekacnet.git] / source4 / torture / raw / lookuprate.c
blob0e8f002efb87c94f579a27f05642073cb7d5b156
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 "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)
38 struct rate_record
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. */
48 { 100, 0, 0},
49 { 1000, 0, 0},
50 { 10000, 0, 0},
51 { 100000, 0, 0}
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))) {
63 return true;
66 return false;
70 static NTSTATUS fill_directory(struct smbcli_tree *tree,
71 const char * path, unsigned count)
73 NTSTATUS status;
74 char *fname = NULL;
75 unsigned i;
76 unsigned current;
78 struct timeval start;
79 struct timeval now;
81 status = smbcli_mkdir(tree, path);
82 if (!NT_STATUS_IS_OK(status)) {
83 return status;
86 printf("filling directory %s with %u files... ", path, count);
87 fflush(stdout);
89 current = random();
90 start = timeval_current();
92 for (i = 0; i < count; ++i) {
93 int fnum;
95 ++current;
96 fname = talloc_asprintf(NULL, "%s\\fill%u",
97 path, current);
99 fnum = smbcli_open(tree, fname, O_RDONLY|O_CREAT,
100 OPENX_MODE_DENY_NONE);
101 if (fnum < 0) {
102 talloc_free(fname);
103 return smbcli_nt_error(tree);
106 smbcli_close(tree, fnum);
107 talloc_free(fname);
110 if (count) {
111 double rate;
112 now = timeval_current();
113 rate = (double)count / usec_to_sec((double)usec_time_diff(&now, &start));
114 printf("%u/sec\n", (unsigned)rate);
115 } else {
116 printf("done\n");
119 return NT_STATUS_OK;
122 static NTSTATUS squash_lookup_error(NTSTATUS status)
124 if (NT_STATUS_IS_OK(status)) {
125 return NT_STATUS_OK;
128 /* We don't care if the file isn't there. */
129 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
130 return NT_STATUS_OK;
133 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
134 return NT_STATUS_OK;
137 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
138 return NT_STATUS_OK;
141 return status;
144 /* Look up a pathname using TRANS2_QUERY_PATH_INFORMATION. */
145 static NTSTATUS querypath_lookup(struct smbcli_tree *tree, const char * path)
147 NTSTATUS status;
148 time_t ftimes[3];
149 size_t fsize;
150 uint16_t fmode;
152 status = smbcli_qpathinfo(tree, path, &ftimes[0], &ftimes[1], &ftimes[2],
153 &fsize, &fmode);
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)
173 NTSTATUS status;
175 struct timeval start;
176 struct timeval now;
177 unsigned count = 0;
178 int64_t elapsed = 0;
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)) {
187 return status;
190 ++count;
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));
198 return NT_STATUS_OK;
201 static bool remove_working_directory(struct smbcli_tree *tree,
202 const char * path)
204 int tries;
206 /* Using smbcli_deltree to delete a very large number of files
207 * doesn't work against all servers. Work around this by
208 * retrying.
210 for (tries = 0; tries < 5; ) {
211 int ret;
213 ret = smbcli_deltree(tree, BASEDIR);
214 if (ret == -1) {
215 tries++;
216 printf("(%s) failed to deltree %s: %s\n",
217 __location__, BASEDIR,
218 smbcli_errstr(tree));
219 continue;
222 return true;
225 return false;
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)
238 NTSTATUS status;
239 bool result = false;
241 int i, tries;
242 struct smbcli_state *cli = NULL;
244 if (!torture_open_connection(&cli, torture, 0)) {
245 goto done;
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));
258 goto done;
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));
266 goto done;
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));
274 goto done;
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)) {
283 goto done;
287 /* Ok. We have run all our tests. Walk through the records we
288 * accumulated and figure out whether the lookups took constant
289 * time of not.
291 for (i = 0; i < ARRAY_SIZE(records); ++i) {
292 if (!fuzzily_equal(records[0].querypath_persec,
293 records[i].querypath_persec,
294 FUZZ_PERCENT)) {
295 printf("querypath rate for %d entries differed by "
296 "more than %d%% from base rate\n",
297 records[i].dirent_count, FUZZ_PERCENT);
298 result = false;
301 if (!fuzzily_equal(records[0].findfirst_persec,
302 records[i].findfirst_persec,
303 FUZZ_PERCENT)) {
304 printf("findfirst rate for %d entries differed by "
305 "more than %d%% from base rate\n",
306 records[i].dirent_count, FUZZ_PERCENT);
307 result = false;
311 done:
312 if (cli) {
313 remove_working_directory(cli->tree, BASEDIR);
314 talloc_free(cli);
317 return result;
320 /* vim: set sts=8 sw=8 : */