2012-12-13 13:31:59 +01:00
/*
2010-12-29 12:50:47 +10:30
Trivial Database : human - readable summary code
Copyright ( C ) Rusty Russell 2010
2012-12-13 13:31:59 +01:00
2010-12-29 12:50:47 +10:30
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
version 3 of the License , or ( at your option ) any later version .
This library 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
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
*/
# include "tdb_private.h"
# define SUMMARY_FORMAT \
2013-02-21 16:34:32 +01:00
" Size of file/data: %llu/%zu \n " \
" Header offset/logical size: %zu/%zu \n " \
2010-12-29 12:50:47 +10:30
" Number of records: %zu \n " \
2013-05-11 00:45:15 +02:00
" Incompatible hash: %s \n " \
2014-02-04 23:35:53 +01:00
" Active/supported feature flags: 0x%08x/0x%08x \n " \
2013-02-21 16:34:32 +01:00
" Robust mutexes locking: %s \n " \
2010-12-29 12:50:47 +10:30
" Smallest/average/largest keys: %zu/%zu/%zu \n " \
" Smallest/average/largest data: %zu/%zu/%zu \n " \
" Smallest/average/largest padding: %zu/%zu/%zu \n " \
" Number of dead records: %zu \n " \
" Smallest/average/largest dead records: %zu/%zu/%zu \n " \
" Number of free records: %zu \n " \
" Smallest/average/largest free records: %zu/%zu/%zu \n " \
" Number of hash chains: %zu \n " \
" Smallest/average/largest hash chains: %zu/%zu/%zu \n " \
" Number of uncoalesced records: %zu \n " \
" Smallest/average/largest uncoalesced runs: %zu/%zu/%zu \n " \
" Percentage keys/data/padding/free/dead/rechdrs&tailers/hashes: %.0f/%.0f/%.0f/%.0f/%.0f/%.0f/%.0f \n "
/* We don't use tally module, to keep upstream happy. */
struct tally {
size_t min , max , total ;
size_t num ;
} ;
static void tally_init ( struct tally * tally )
{
tally - > total = 0 ;
tally - > num = 0 ;
tally - > min = tally - > max = 0 ;
}
static void tally_add ( struct tally * tally , size_t len )
{
if ( tally - > num = = 0 )
tally - > max = tally - > min = len ;
else if ( len > tally - > max )
tally - > max = len ;
else if ( len < tally - > min )
tally - > min = len ;
tally - > num + + ;
tally - > total + = len ;
}
static size_t tally_mean ( const struct tally * tally )
{
if ( ! tally - > num )
return 0 ;
return tally - > total / tally - > num ;
}
static size_t get_hash_length ( struct tdb_context * tdb , unsigned int i )
{
tdb_off_t rec_ptr ;
2018-10-04 16:42:45 +02:00
struct tdb_chainwalk_ctx chainwalk ;
2010-12-29 12:50:47 +10:30
size_t count = 0 ;
if ( tdb_ofs_read ( tdb , TDB_HASH_TOP ( i ) , & rec_ptr ) = = - 1 )
return 0 ;
2018-10-04 16:42:45 +02:00
tdb_chainwalk_init ( & chainwalk , rec_ptr ) ;
2010-12-29 12:50:47 +10:30
/* keep looking until we find the right record */
while ( rec_ptr ) {
struct tdb_record r ;
2018-10-04 16:42:45 +02:00
bool ok ;
2010-12-29 12:50:47 +10:30
+ + count ;
if ( tdb_rec_read ( tdb , rec_ptr , & r ) = = - 1 )
return 0 ;
rec_ptr = r . next ;
2018-10-04 16:42:45 +02:00
ok = tdb_chainwalk_check ( tdb , & chainwalk , rec_ptr ) ;
if ( ! ok ) {
return SIZE_MAX ;
}
2010-12-29 12:50:47 +10:30
}
return count ;
}
_PUBLIC_ char * tdb_summary ( struct tdb_context * tdb )
{
2013-02-21 16:34:32 +01:00
off_t file_size ;
2011-04-19 21:00:59 +09:30
tdb_off_t off , rec_off ;
2013-12-10 17:47:05 +01:00
struct tally freet , keys , data , dead , extra , hashval , uncoal ;
2010-12-29 12:50:47 +10:30
struct tdb_record rec ;
char * ret = NULL ;
bool locked ;
2014-05-13 03:15:41 +02:00
size_t unc = 0 ;
int len ;
2011-04-19 21:00:59 +09:30
struct tdb_record recovery ;
2010-12-29 12:50:47 +10:30
/* Read-only databases use no locking at all: it's best-effort.
* We may have a write lock already , so skip that case too . */
if ( tdb - > read_only | | tdb - > allrecord_lock . count ! = 0 ) {
locked = false ;
} else {
if ( tdb_lockall_read ( tdb ) = = - 1 )
return NULL ;
locked = true ;
}
2011-04-19 21:00:59 +09:30
if ( tdb_recovery_area ( tdb , tdb - > methods , & rec_off , & recovery ) ! = 0 ) {
goto unlock ;
}
2010-12-29 12:50:47 +10:30
tally_init ( & freet ) ;
tally_init ( & keys ) ;
tally_init ( & data ) ;
tally_init ( & dead ) ;
tally_init ( & extra ) ;
2013-12-10 17:47:05 +01:00
tally_init ( & hashval ) ;
2010-12-29 12:50:47 +10:30
tally_init ( & uncoal ) ;
2012-12-20 16:36:02 +01:00
for ( off = TDB_DATA_START ( tdb - > hash_size ) ;
2010-12-29 12:50:47 +10:30
off < tdb - > map_size - 1 ;
off + = sizeof ( rec ) + rec . rec_len ) {
if ( tdb - > methods - > tdb_read ( tdb , off , & rec , sizeof ( rec ) ,
DOCONV ( ) ) = = - 1 )
goto unlock ;
switch ( rec . magic ) {
case TDB_MAGIC :
tally_add ( & keys , rec . key_len ) ;
tally_add ( & data , rec . data_len ) ;
tally_add ( & extra , rec . rec_len - ( rec . key_len
+ rec . data_len ) ) ;
if ( unc > 1 )
tally_add ( & uncoal , unc - 1 ) ;
unc = 0 ;
break ;
case TDB_FREE_MAGIC :
tally_add ( & freet , rec . rec_len ) ;
unc + + ;
break ;
/* If we crash after ftruncate, we can get zeroes or fill. */
case TDB_RECOVERY_INVALID_MAGIC :
case 0x42424242 :
unc + + ;
2011-04-19 21:00:59 +09:30
/* If it's a valid recovery, we can trust rec_len. */
if ( off ! = rec_off ) {
rec . rec_len = tdb_dead_space ( tdb , off )
- sizeof ( rec ) ;
}
2017-07-26 18:28:12 +02:00
FALL_THROUGH ;
2010-12-29 12:50:47 +10:30
case TDB_DEAD_MAGIC :
tally_add ( & dead , rec . rec_len ) ;
break ;
default :
TDB_LOG ( ( tdb , TDB_DEBUG_ERROR ,
2013-05-28 16:53:56 +09:30
" Unexpected record magic 0x%x at offset %u \n " ,
2010-12-29 12:50:47 +10:30
rec . magic , off ) ) ;
goto unlock ;
}
}
if ( unc > 1 )
tally_add ( & uncoal , unc - 1 ) ;
2012-12-20 16:36:02 +01:00
for ( off = 0 ; off < tdb - > hash_size ; off + + )
2013-12-10 17:47:05 +01:00
tally_add ( & hashval , get_hash_length ( tdb , off ) ) ;
2010-12-29 12:50:47 +10:30
2013-02-21 16:34:32 +01:00
file_size = tdb - > hdr_ofs + tdb - > map_size ;
2010-12-29 12:50:47 +10:30
2014-05-13 03:15:41 +02:00
len = asprintf ( & ret , SUMMARY_FORMAT ,
2013-02-21 16:34:32 +01:00
( unsigned long long ) file_size , keys . total + data . total ,
( size_t ) tdb - > hdr_ofs , ( size_t ) tdb - > map_size ,
2011-03-30 06:51:40 +02:00
keys . num ,
2013-05-11 00:45:15 +02:00
( tdb - > hash_fn = = tdb_jenkins_hash ) ? " yes " : " no " ,
2014-02-04 23:35:53 +01:00
( unsigned ) tdb - > feature_flags , TDB_SUPPORTED_FEATURE_FLAGS ,
2013-02-21 16:34:32 +01:00
( tdb - > feature_flags & TDB_FEATURE_FLAG_MUTEX ) ? " yes " : " no " ,
2011-03-30 06:51:40 +02:00
keys . min , tally_mean ( & keys ) , keys . max ,
data . min , tally_mean ( & data ) , data . max ,
extra . min , tally_mean ( & extra ) , extra . max ,
dead . num ,
dead . min , tally_mean ( & dead ) , dead . max ,
freet . num ,
freet . min , tally_mean ( & freet ) , freet . max ,
2013-12-10 17:47:05 +01:00
hashval . num ,
hashval . min , tally_mean ( & hashval ) , hashval . max ,
2011-03-30 06:51:40 +02:00
uncoal . total ,
uncoal . min , tally_mean ( & uncoal ) , uncoal . max ,
2013-02-21 16:34:32 +01:00
keys . total * 100.0 / file_size ,
data . total * 100.0 / file_size ,
extra . total * 100.0 / file_size ,
freet . total * 100.0 / file_size ,
dead . total * 100.0 / file_size ,
2011-03-30 06:51:40 +02:00
( keys . num + freet . num + dead . num )
* ( sizeof ( struct tdb_record ) + sizeof ( uint32_t ) )
2013-02-21 16:34:32 +01:00
* 100.0 / file_size ,
2012-12-20 16:36:02 +01:00
tdb - > hash_size * sizeof ( tdb_off_t )
2013-02-21 16:34:32 +01:00
* 100.0 / file_size ) ;
2014-05-13 03:15:41 +02:00
if ( len = = - 1 ) {
goto unlock ;
}
2010-12-29 12:50:47 +10:30
unlock :
if ( locked ) {
tdb_unlockall_read ( tdb ) ;
}
return ret ;
}