~zulcss/samba/server-dailies-3.4

« back to all changes in this revision

Viewing changes to source4/torture/raw/lookuprate.c

  • Committer: Chuck Short
  • Date: 2010-09-28 20:38:39 UTC
  • Revision ID: zulcss@ubuntu.com-20100928203839-pgjulytsi9ue63x1
Initial version

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
   File lookup rate test.
 
3
 
 
4
   Copyright (C) James Peach 2006
 
5
 
 
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.
 
10
 
 
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.
 
15
 
 
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/>.
 
18
*/
 
19
 
 
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"
 
28
 
 
29
#define BASEDIR "\\lookuprate"
 
30
#define MISSINGNAME BASEDIR "\\foo"
 
31
 
 
32
#define FUZZ_PERCENT 10
 
33
 
 
34
#define usec_to_sec(s) ((s) / 1000000)
 
35
#define sec_to_usec(s) ((s) * 1000000)
 
36
 
 
37
struct rate_record
 
38
{
 
39
    unsigned    dirent_count;
 
40
    unsigned    querypath_persec;
 
41
    unsigned    findfirst_persec;
 
42
};
 
43
 
 
44
static struct rate_record records[] =
 
45
{
 
46
    { 0, 0, 0 },        /* Base (optimal) lookup rate. */
 
47
    { 100, 0, 0},
 
48
    { 1000, 0, 0},
 
49
    { 10000, 0, 0},
 
50
    { 100000, 0, 0}
 
51
};
 
52
 
 
53
typedef NTSTATUS lookup_function(struct smbcli_tree *tree, const char * path);
 
54
 
 
55
/* Test whether rhs is within fuzz% of lhs. */
 
56
static bool fuzzily_equal(unsigned lhs, unsigned rhs, int percent)
 
57
{
 
58
        double fuzz = (double)lhs * (double)percent/100.0;
 
59
 
 
60
        if (((double)rhs >= ((double)lhs - fuzz)) &&
 
61
            ((double)rhs <= ((double)lhs + fuzz))) {
 
62
                return true;
 
63
        }
 
64
 
 
65
        return false;
 
66
 
 
67
}
 
68
 
 
69
static NTSTATUS fill_directory(struct smbcli_tree *tree,
 
70
            const char * path, unsigned count)
 
71
{
 
72
        NTSTATUS        status;
 
73
        char            *fname = NULL;
 
74
        unsigned        i;
 
75
        unsigned        current;
 
76
 
 
77
        struct timeval start;
 
78
        struct timeval now;
 
79
 
 
80
        status = smbcli_mkdir(tree, path);
 
81
        if (!NT_STATUS_IS_OK(status)) {
 
82
                return status;
 
83
        }
 
84
 
 
85
        printf("filling directory %s with %u files... ", path, count);
 
86
        fflush(stdout);
 
87
 
 
88
        current = random();
 
89
        start = timeval_current();
 
90
 
 
91
        for (i = 0; i < count; ++i) {
 
92
                int fnum;
 
93
 
 
94
                ++current;
 
95
                fname = talloc_asprintf(NULL, "%s\\fill%u",
 
96
                                    path, current);
 
97
 
 
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);
 
103
                }
 
104
 
 
105
                smbcli_close(tree, fnum);
 
106
                talloc_free(fname);
 
107
        }
 
108
 
 
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");
 
116
        }
 
117
 
 
118
        return NT_STATUS_OK;
 
119
}
 
120
 
 
121
static NTSTATUS squash_lookup_error(NTSTATUS status)
 
122
{
 
123
        if (NT_STATUS_IS_OK(status)) {
 
124
                return NT_STATUS_OK;
 
125
        }
 
126
 
 
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;
 
130
        }
 
131
 
 
132
        if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
 
133
                return NT_STATUS_OK;
 
134
        }
 
135
 
 
136
        if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_FILE)) {
 
137
                return NT_STATUS_OK;
 
138
        }
 
139
 
 
140
        return status;
 
141
}
 
142
 
 
143
/* Look up a pathname using TRANS2_QUERY_PATH_INFORMATION. */
 
144
static NTSTATUS querypath_lookup(struct smbcli_tree *tree, const char * path)
 
145
{
 
146
        NTSTATUS        status;
 
147
        time_t          ftimes[3];
 
148
        size_t          fsize;
 
149
        uint16_t        fmode;
 
150
 
 
151
        status = smbcli_qpathinfo(tree, path, &ftimes[0], &ftimes[1], &ftimes[2],
 
152
                        &fsize, &fmode);
 
153
 
 
154
        return squash_lookup_error(status);
 
155
}
 
156
 
 
157
/* Look up a pathname using TRANS2_FIND_FIRST2. */
 
158
static NTSTATUS findfirst_lookup(struct smbcli_tree *tree, const char * path)
 
159
{
 
160
        NTSTATUS status = NT_STATUS_OK;
 
161
 
 
162
        if (smbcli_list(tree, path, 0, NULL, NULL) < 0) {
 
163
                status = smbcli_nt_error(tree);
 
164
        }
 
165
 
 
166
        return squash_lookup_error(status);
 
167
}
 
168
 
 
169
static NTSTATUS lookup_rate_convert(struct smbcli_tree *tree,
 
170
        lookup_function lookup, const char * path, unsigned * rate)
 
171
{
 
172
        NTSTATUS        status;
 
173
 
 
174
        struct timeval  start;
 
175
        struct timeval  now;
 
176
        unsigned        count = 0;
 
177
        int64_t         elapsed = 0;
 
178
 
 
179
#define LOOKUP_PERIOD_SEC (2)
 
180
 
 
181
        start = timeval_current();
 
182
        while (elapsed < sec_to_usec(LOOKUP_PERIOD_SEC)) {
 
183
 
 
184
                status = lookup(tree, path);
 
185
                if (!NT_STATUS_IS_OK(status)) {
 
186
                        return status;
 
187
                }
 
188
 
 
189
                ++count;
 
190
                now = timeval_current();
 
191
                elapsed = usec_time_diff(&now, &start);
 
192
        }
 
193
 
 
194
#undef LOOKUP_PERIOD_SEC
 
195
 
 
196
        *rate = (unsigned)((double)count / (double)usec_to_sec(elapsed));
 
197
        return NT_STATUS_OK;
 
198
}
 
199
 
 
200
static bool remove_working_directory(struct smbcli_tree *tree,
 
201
                const char * path)
 
202
{
 
203
        int tries;
 
204
 
 
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.
 
208
         */
 
209
        for (tries = 0; tries < 5; ) {
 
210
                int ret;
 
211
 
 
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;
 
219
                }
 
220
 
 
221
                return true;
 
222
        }
 
223
 
 
224
        return false;
 
225
 
 
226
}
 
227
 
 
228
/* Verify that looking up a file name takes constant time.
 
229
 *
 
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.
 
234
 */
 
235
bool torture_bench_lookup(struct torture_context *torture)
 
236
{
 
237
        NTSTATUS        status;
 
238
        bool            result = false;
 
239
 
 
240
        int i;
 
241
        struct smbcli_state *cli = NULL;
 
242
 
 
243
        if (!torture_open_connection(&cli, torture, 0)) {
 
244
                goto done;
 
245
        }
 
246
 
 
247
        remove_working_directory(cli->tree, BASEDIR);
 
248
 
 
249
        for (i = 0; i < ARRAY_SIZE(records); ++i) {
 
250
                printf("testing lookup rate with %u directory entries\n",
 
251
                                records[i].dirent_count);
 
252
 
 
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;
 
258
                }
 
259
 
 
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;
 
266
                }
 
267
 
 
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;
 
274
                }
 
275
 
 
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);
 
280
 
 
281
                if (!remove_working_directory(cli->tree, BASEDIR)) {
 
282
                        goto done;
 
283
                }
 
284
        }
 
285
 
 
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.
 
289
         */
 
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;
 
298
                }
 
299
 
 
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;
 
307
                }
 
308
        }
 
309
 
 
310
done:
 
311
        if (cli) {
 
312
                remove_working_directory(cli->tree, BASEDIR);
 
313
                talloc_free(cli);
 
314
        }
 
315
 
 
316
        return result;
 
317
}
 
318
 
 
319
/* vim: set sts=8 sw=8 : */