docs: man smbtorture: Add missing meta data.
[Samba/gebeck_regimport.git] / source4 / torture / raw / lookuprate.c
blob7c0251a0f7a8a2b631ef0b594d36cd8a3558cd2a
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"
25 #include "torture/raw/proto.h"
27 #define BASEDIR "\\lookuprate"
28 #define MISSINGNAME BASEDIR "\\foo"
30 #define FUZZ_PERCENT 10
32 #define usec_to_sec(s) ((s) / 1000000)
33 #define sec_to_usec(s) ((s) * 1000000)
35 struct rate_record
37 unsigned dirent_count;
38 unsigned querypath_persec;
39 unsigned findfirst_persec;
42 static struct rate_record records[] =
44 { 0, 0, 0 }, /* Base (optimal) lookup rate. */
45 { 100, 0, 0},
46 { 1000, 0, 0},
47 { 10000, 0, 0},
48 { 100000, 0, 0}
51 typedef NTSTATUS lookup_function(struct smbcli_tree *tree, const char * path);
53 /* Test whether rhs is within fuzz% of lhs. */
54 static bool fuzzily_equal(unsigned lhs, unsigned rhs, int percent)
56 double fuzz = (double)lhs * (double)percent/100.0;
58 if (((double)rhs >= ((double)lhs - fuzz)) &&
59 ((double)rhs <= ((double)lhs + fuzz))) {
60 return true;
63 return false;
67 static NTSTATUS fill_directory(struct smbcli_tree *tree,
68 const char * path, unsigned count)
70 NTSTATUS status;
71 char *fname = NULL;
72 unsigned i;
73 unsigned current;
75 struct timeval start;
76 struct timeval now;
78 status = smbcli_mkdir(tree, path);
79 if (!NT_STATUS_IS_OK(status)) {
80 return status;
83 printf("filling directory %s with %u files... ", path, count);
84 fflush(stdout);
86 current = random();
87 start = timeval_current();
89 for (i = 0; i < count; ++i) {
90 int fnum;
92 ++current;
93 fname = talloc_asprintf(NULL, "%s\\fill%u",
94 path, current);
96 fnum = smbcli_open(tree, fname, O_RDONLY|O_CREAT,
97 DENY_NONE);
98 if (fnum < 0) {
99 talloc_free(fname);
100 return smbcli_nt_error(tree);
103 smbcli_close(tree, fnum);
104 talloc_free(fname);
107 if (count) {
108 double rate;
109 now = timeval_current();
110 rate = (double)count / usec_to_sec((double)usec_time_diff(&now, &start));
111 printf("%u/sec\n", (unsigned)rate);
112 } else {
113 printf("done\n");
116 return NT_STATUS_OK;
119 static NTSTATUS squash_lookup_error(NTSTATUS status)
121 if (NT_STATUS_IS_OK(status)) {
122 return NT_STATUS_OK;
125 /* We don't care if the file isn't there. */
126 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_NOT_FOUND)) {
127 return NT_STATUS_OK;
130 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
131 return NT_STATUS_OK;
134 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
135 return NT_STATUS_OK;
138 return status;
141 /* Look up a pathname using TRANS2_QUERY_PATH_INFORMATION. */
142 static NTSTATUS querypath_lookup(struct smbcli_tree *tree, const char * path)
144 NTSTATUS status;
145 time_t ftimes[3];
146 size_t fsize;
147 uint16_t fmode;
149 status = smbcli_qpathinfo(tree, path, &ftimes[0], &ftimes[1], &ftimes[2],
150 &fsize, &fmode);
152 return squash_lookup_error(status);
155 /* Look up a pathname using TRANS2_FIND_FIRST2. */
156 static NTSTATUS findfirst_lookup(struct smbcli_tree *tree, const char * path)
158 NTSTATUS status = NT_STATUS_OK;
160 if (smbcli_list(tree, path, 0, NULL, NULL) < 0) {
161 status = smbcli_nt_error(tree);
164 return squash_lookup_error(status);
167 static NTSTATUS lookup_rate_convert(struct smbcli_tree *tree,
168 lookup_function lookup, const char * path, unsigned * rate)
170 NTSTATUS status;
172 struct timeval start;
173 struct timeval now;
174 unsigned count = 0;
175 int64_t elapsed = 0;
177 #define LOOKUP_PERIOD_SEC (2)
179 start = timeval_current();
180 while (elapsed < sec_to_usec(LOOKUP_PERIOD_SEC)) {
182 status = lookup(tree, path);
183 if (!NT_STATUS_IS_OK(status)) {
184 return status;
187 ++count;
188 now = timeval_current();
189 elapsed = usec_time_diff(&now, &start);
192 #undef LOOKUP_PERIOD_SEC
194 *rate = (unsigned)((double)count / (double)usec_to_sec(elapsed));
195 return NT_STATUS_OK;
198 static bool remove_working_directory(struct smbcli_tree *tree,
199 const char * path)
201 int tries;
203 /* Using smbcli_deltree to delete a very large number of files
204 * doesn't work against all servers. Work around this by
205 * retrying.
207 for (tries = 0; tries < 5; ) {
208 int ret;
210 ret = smbcli_deltree(tree, BASEDIR);
211 if (ret == -1) {
212 tries++;
213 printf("(%s) failed to deltree %s: %s\n",
214 __location__, BASEDIR,
215 smbcli_errstr(tree));
216 continue;
219 return true;
222 return false;
226 /* Verify that looking up a file name takes constant time.
228 * This test samples the lookup rate for a non-existent filename in a
229 * directory, while varying the number of files in the directory. The
230 * lookup rate should continue to approximate the lookup rate for the
231 * empty directory case.
233 bool torture_bench_lookup(struct torture_context *torture)
235 NTSTATUS status;
236 bool result = false;
238 int i;
239 struct smbcli_state *cli = NULL;
241 if (!torture_open_connection(&cli, torture, 0)) {
242 goto done;
245 remove_working_directory(cli->tree, BASEDIR);
247 for (i = 0; i < ARRAY_SIZE(records); ++i) {
248 printf("Testing lookup rate with %u directory entries\n",
249 records[i].dirent_count);
251 status = fill_directory(cli->tree, BASEDIR,
252 records[i].dirent_count);
253 if (!NT_STATUS_IS_OK(status)) {
254 printf("failed to fill directory: %s\n", nt_errstr(status));
255 goto done;
258 status = lookup_rate_convert(cli->tree, querypath_lookup,
259 MISSINGNAME, &records[i].querypath_persec);
260 if (!NT_STATUS_IS_OK(status)) {
261 printf("querypathinfo of %s failed: %s\n",
262 MISSINGNAME, nt_errstr(status));
263 goto done;
266 status = lookup_rate_convert(cli->tree, findfirst_lookup,
267 MISSINGNAME, &records[i].findfirst_persec);
268 if (!NT_STATUS_IS_OK(status)) {
269 printf("findfirst of %s failed: %s\n",
270 MISSINGNAME, nt_errstr(status));
271 goto done;
274 printf("entries = %u, querypath = %u/sec, findfirst = %u/sec\n",
275 records[i].dirent_count,
276 records[i].querypath_persec,
277 records[i].findfirst_persec);
279 if (!remove_working_directory(cli->tree, BASEDIR)) {
280 goto done;
284 /* Ok. We have run all our tests. Walk through the records we
285 * accumulated and figure out whether the lookups took constant
286 * time of not.
288 for (i = 0; i < ARRAY_SIZE(records); ++i) {
289 if (!fuzzily_equal(records[0].querypath_persec,
290 records[i].querypath_persec,
291 FUZZ_PERCENT)) {
292 printf("querypath rate for %d entries differed by "
293 "more than %d%% from base rate\n",
294 records[i].dirent_count, FUZZ_PERCENT);
295 result = false;
298 if (!fuzzily_equal(records[0].findfirst_persec,
299 records[i].findfirst_persec,
300 FUZZ_PERCENT)) {
301 printf("findfirst rate for %d entries differed by "
302 "more than %d%% from base rate\n",
303 records[i].dirent_count, FUZZ_PERCENT);
304 result = false;
308 done:
309 if (cli) {
310 remove_working_directory(cli->tree, BASEDIR);
311 talloc_free(cli);
314 return result;
317 /* vim: set sts=8 sw=8 : */