2008-04-22 02:09:43 +04:00
/*
File lookup rate test .
Copyright ( C ) James Peach 2006
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "system/filesys.h"
2008-04-27 17:02:46 +04:00
# include "torture/smbtorture.h"
2008-04-22 02:09:43 +04:00
# include "libcli/libcli.h"
2008-04-22 03:53:15 +04:00
# include "torture/util.h"
2011-03-19 02:42:42 +03:00
# include "torture/raw/proto.h"
2008-04-22 02:09:43 +04:00
# define BASEDIR "\\lookuprate"
# define MISSINGNAME BASEDIR "\\foo"
# define FUZZ_PERCENT 10
# define usec_to_sec(s) ((s) / 1000000)
# define sec_to_usec(s) ((s) * 1000000)
struct rate_record
{
unsigned dirent_count ;
unsigned querypath_persec ;
unsigned findfirst_persec ;
} ;
static struct rate_record records [ ] =
{
{ 0 , 0 , 0 } , /* Base (optimal) lookup rate. */
{ 100 , 0 , 0 } ,
{ 1000 , 0 , 0 } ,
{ 10000 , 0 , 0 } ,
{ 100000 , 0 , 0 }
} ;
typedef NTSTATUS lookup_function ( struct smbcli_tree * tree , const char * path ) ;
/* Test whether rhs is within fuzz% of lhs. */
static bool fuzzily_equal ( unsigned lhs , unsigned rhs , int percent )
{
double fuzz = ( double ) lhs * ( double ) percent / 100.0 ;
if ( ( ( double ) rhs > = ( ( double ) lhs - fuzz ) ) & &
( ( double ) rhs < = ( ( double ) lhs + fuzz ) ) ) {
return true ;
}
return false ;
}
static NTSTATUS fill_directory ( struct smbcli_tree * tree ,
const char * path , unsigned count )
{
NTSTATUS status ;
char * fname = NULL ;
unsigned i ;
unsigned current ;
struct timeval start ;
struct timeval now ;
status = smbcli_mkdir ( tree , path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
printf ( " filling directory %s with %u files... " , path , count ) ;
fflush ( stdout ) ;
current = random ( ) ;
start = timeval_current ( ) ;
for ( i = 0 ; i < count ; + + i ) {
int fnum ;
+ + current ;
fname = talloc_asprintf ( NULL , " %s \\ fill%u " ,
path , current ) ;
fnum = smbcli_open ( tree , fname , O_RDONLY | O_CREAT ,
2009-11-19 01:42:31 +03:00
DENY_NONE ) ;
2008-04-22 02:09:43 +04:00
if ( fnum < 0 ) {
talloc_free ( fname ) ;
return smbcli_nt_error ( tree ) ;
}
smbcli_close ( tree , fnum ) ;
talloc_free ( fname ) ;
}
if ( count ) {
double rate ;
now = timeval_current ( ) ;
rate = ( double ) count / usec_to_sec ( ( double ) usec_time_diff ( & now , & start ) ) ;
printf ( " %u/sec \n " , ( unsigned ) rate ) ;
} else {
printf ( " done \n " ) ;
}
return NT_STATUS_OK ;
}
static NTSTATUS squash_lookup_error ( NTSTATUS status )
{
if ( NT_STATUS_IS_OK ( status ) ) {
return NT_STATUS_OK ;
}
/* We don't care if the file isn't there. */
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_PATH_NOT_FOUND ) ) {
return NT_STATUS_OK ;
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ) {
return NT_STATUS_OK ;
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NO_SUCH_FILE ) ) {
return NT_STATUS_OK ;
}
return status ;
}
/* Look up a pathname using TRANS2_QUERY_PATH_INFORMATION. */
static NTSTATUS querypath_lookup ( struct smbcli_tree * tree , const char * path )
{
NTSTATUS status ;
time_t ftimes [ 3 ] ;
size_t fsize ;
uint16_t fmode ;
status = smbcli_qpathinfo ( tree , path , & ftimes [ 0 ] , & ftimes [ 1 ] , & ftimes [ 2 ] ,
& fsize , & fmode ) ;
return squash_lookup_error ( status ) ;
}
/* Look up a pathname using TRANS2_FIND_FIRST2. */
static NTSTATUS findfirst_lookup ( struct smbcli_tree * tree , const char * path )
{
NTSTATUS status = NT_STATUS_OK ;
if ( smbcli_list ( tree , path , 0 , NULL , NULL ) < 0 ) {
status = smbcli_nt_error ( tree ) ;
}
return squash_lookup_error ( status ) ;
}
static NTSTATUS lookup_rate_convert ( struct smbcli_tree * tree ,
lookup_function lookup , const char * path , unsigned * rate )
{
NTSTATUS status ;
struct timeval start ;
struct timeval now ;
unsigned count = 0 ;
int64_t elapsed = 0 ;
# define LOOKUP_PERIOD_SEC (2)
start = timeval_current ( ) ;
while ( elapsed < sec_to_usec ( LOOKUP_PERIOD_SEC ) ) {
status = lookup ( tree , path ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
+ + count ;
now = timeval_current ( ) ;
elapsed = usec_time_diff ( & now , & start ) ;
}
# undef LOOKUP_PERIOD_SEC
* rate = ( unsigned ) ( ( double ) count / ( double ) usec_to_sec ( elapsed ) ) ;
return NT_STATUS_OK ;
}
static bool remove_working_directory ( struct smbcli_tree * tree ,
const char * path )
{
int tries ;
/* Using smbcli_deltree to delete a very large number of files
* doesn ' t work against all servers . Work around this by
* retrying .
*/
for ( tries = 0 ; tries < 5 ; ) {
int ret ;
ret = smbcli_deltree ( tree , BASEDIR ) ;
if ( ret = = - 1 ) {
tries + + ;
printf ( " (%s) failed to deltree %s: %s \n " ,
__location__ , BASEDIR ,
smbcli_errstr ( tree ) ) ;
continue ;
}
return true ;
}
return false ;
}
/* Verify that looking up a file name takes constant time.
*
2012-06-11 19:58:02 +04:00
* This test samples the lookup rate for a non - existent filename in a
2008-04-22 02:09:43 +04:00
* directory , while varying the number of files in the directory . The
* lookup rate should continue to approximate the lookup rate for the
* empty directory case .
*/
bool torture_bench_lookup ( struct torture_context * torture )
{
NTSTATUS status ;
bool result = false ;
2008-12-24 01:22:57 +03:00
int i ;
2008-04-22 02:09:43 +04:00
struct smbcli_state * cli = NULL ;
if ( ! torture_open_connection ( & cli , torture , 0 ) ) {
goto done ;
}
remove_working_directory ( cli - > tree , BASEDIR ) ;
for ( i = 0 ; i < ARRAY_SIZE ( records ) ; + + i ) {
2010-04-11 03:39:06 +04:00
printf ( " Testing lookup rate with %u directory entries \n " ,
2008-04-22 02:09:43 +04:00
records [ i ] . dirent_count ) ;
status = fill_directory ( cli - > tree , BASEDIR ,
records [ i ] . dirent_count ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " failed to fill directory: %s \n " , nt_errstr ( status ) ) ;
goto done ;
}
status = lookup_rate_convert ( cli - > tree , querypath_lookup ,
MISSINGNAME , & records [ i ] . querypath_persec ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " querypathinfo of %s failed: %s \n " ,
MISSINGNAME , nt_errstr ( status ) ) ;
goto done ;
}
status = lookup_rate_convert ( cli - > tree , findfirst_lookup ,
MISSINGNAME , & records [ i ] . findfirst_persec ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " findfirst of %s failed: %s \n " ,
MISSINGNAME , nt_errstr ( status ) ) ;
goto done ;
}
printf ( " entries = %u, querypath = %u/sec, findfirst = %u/sec \n " ,
records [ i ] . dirent_count ,
records [ i ] . querypath_persec ,
records [ i ] . findfirst_persec ) ;
if ( ! remove_working_directory ( cli - > tree , BASEDIR ) ) {
goto done ;
}
}
/* Ok. We have run all our tests. Walk through the records we
* accumulated and figure out whether the lookups took constant
* time of not .
*/
for ( i = 0 ; i < ARRAY_SIZE ( records ) ; + + i ) {
if ( ! fuzzily_equal ( records [ 0 ] . querypath_persec ,
records [ i ] . querypath_persec ,
FUZZ_PERCENT ) ) {
printf ( " querypath rate for %d entries differed by "
" more than %d%% from base rate \n " ,
records [ i ] . dirent_count , FUZZ_PERCENT ) ;
result = false ;
}
if ( ! fuzzily_equal ( records [ 0 ] . findfirst_persec ,
records [ i ] . findfirst_persec ,
FUZZ_PERCENT ) ) {
printf ( " findfirst rate for %d entries differed by "
" more than %d%% from base rate \n " ,
records [ i ] . dirent_count , FUZZ_PERCENT ) ;
result = false ;
}
}
done :
if ( cli ) {
remove_working_directory ( cli - > tree , BASEDIR ) ;
talloc_free ( cli ) ;
}
return result ;
}
/* vim: set sts=8 sw=8 : */