root/source4/torture/raw/lookuprate.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fuzzily_equal
  2. fill_directory
  3. squash_lookup_error
  4. querypath_lookup
  5. findfirst_lookup
  6. lookup_rate_convert
  7. remove_working_directory
  8. torture_bench_lookup

   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)
     /* [<][>][^][v][top][bottom][index][help] */
  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,
     /* [<][>][^][v][top][bottom][index][help] */
  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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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,
     /* [<][>][^][v][top][bottom][index][help] */
 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)
     /* [<][>][^][v][top][bottom][index][help] */
 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 : */

/* [<][>][^][v][top][bottom][index][help] */