LCOV - code coverage report
Current view: top level - lib/tdb/common - summary.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 87 119 73.1 %
Date: 2024-04-21 15:09:00 Functions: 5 5 100.0 %

          Line data    Source code
       1             :  /*
       2             :    Trivial Database: human-readable summary code
       3             :    Copyright (C) Rusty Russell 2010
       4             : 
       5             :    This library is free software; you can redistribute it and/or
       6             :    modify it under the terms of the GNU Lesser General Public
       7             :    License as published by the Free Software Foundation; either
       8             :    version 3 of the License, or (at your option) any later version.
       9             : 
      10             :    This library is distributed in the hope that it will be useful,
      11             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13             :    Lesser General Public License for more details.
      14             : 
      15             :    You should have received a copy of the GNU Lesser General Public
      16             :    License along with this library; if not, see <http://www.gnu.org/licenses/>.
      17             : */
      18             : #include "tdb_private.h"
      19             : 
      20             : #define SUMMARY_FORMAT \
      21             :         "Size of file/data: %llu/%zu\n" \
      22             :         "Header offset/logical size: %zu/%zu\n" \
      23             :         "Number of records: %zu\n" \
      24             :         "Incompatible hash: %s\n" \
      25             :         "Active/supported feature flags: 0x%08x/0x%08x\n" \
      26             :         "Robust mutexes locking: %s\n" \
      27             :         "Smallest/average/largest keys: %zu/%zu/%zu\n" \
      28             :         "Smallest/average/largest data: %zu/%zu/%zu\n" \
      29             :         "Smallest/average/largest padding: %zu/%zu/%zu\n" \
      30             :         "Number of dead records: %zu\n" \
      31             :         "Smallest/average/largest dead records: %zu/%zu/%zu\n" \
      32             :         "Number of free records: %zu\n" \
      33             :         "Smallest/average/largest free records: %zu/%zu/%zu\n" \
      34             :         "Number of hash chains: %zu\n" \
      35             :         "Smallest/average/largest hash chains: %zu/%zu/%zu\n" \
      36             :         "Number of uncoalesced records: %zu\n" \
      37             :         "Smallest/average/largest uncoalesced runs: %zu/%zu/%zu\n" \
      38             :         "Percentage keys/data/padding/free/dead/rechdrs&tailers/hashes: %.0f/%.0f/%.0f/%.0f/%.0f/%.0f/%.0f\n"
      39             : 
      40             : /* We don't use tally module, to keep upstream happy. */
      41             : struct tally {
      42             :         size_t min, max, total;
      43             :         size_t num;
      44             : };
      45             : 
      46          42 : static void tally_init(struct tally *tally)
      47             : {
      48          42 :         tally->total = 0;
      49          42 :         tally->num = 0;
      50          42 :         tally->min = tally->max = 0;
      51          42 : }
      52             : 
      53        9792 : static void tally_add(struct tally *tally, size_t len)
      54             : {
      55        9792 :         if (tally->num == 0)
      56          30 :                 tally->max = tally->min = len;
      57        9762 :         else if (len > tally->max)
      58          22 :                 tally->max = len;
      59        9740 :         else if (len < tally->min)
      60          36 :                 tally->min = len;
      61        9792 :         tally->num++;
      62        9792 :         tally->total += len;
      63        9792 : }
      64             : 
      65          42 : static size_t tally_mean(const struct tally *tally)
      66             : {
      67          42 :         if (!tally->num)
      68          12 :                 return 0;
      69          30 :         return tally->total / tally->num;
      70             : }
      71             : 
      72         786 : static size_t get_hash_length(struct tdb_context *tdb, unsigned int i)
      73             : {
      74           0 :         tdb_off_t rec_ptr;
      75           0 :         struct tdb_chainwalk_ctx chainwalk;
      76         786 :         size_t count = 0;
      77             : 
      78         786 :         if (tdb_ofs_read(tdb, TDB_HASH_TOP(i), &rec_ptr) == -1)
      79           0 :                 return 0;
      80             : 
      81         786 :         tdb_chainwalk_init(&chainwalk, rec_ptr);
      82             : 
      83             :         /* keep looking until we find the right record */
      84        3786 :         while (rec_ptr) {
      85           0 :                 struct tdb_record r;
      86           0 :                 bool ok;
      87        3000 :                 ++count;
      88        3000 :                 if (tdb_rec_read(tdb, rec_ptr, &r) == -1)
      89           0 :                         return 0;
      90        3000 :                 rec_ptr = r.next;
      91        3000 :                 ok = tdb_chainwalk_check(tdb, &chainwalk, rec_ptr);
      92        3000 :                 if (!ok) {
      93           0 :                         return SIZE_MAX;
      94             :                 }
      95             :         }
      96         786 :         return count;
      97             : }
      98             : 
      99           6 : _PUBLIC_ char *tdb_summary(struct tdb_context *tdb)
     100             : {
     101           0 :         off_t file_size;
     102           0 :         tdb_off_t off, rec_off;
     103           0 :         struct tally freet, keys, data, dead, extra, hashval, uncoal;
     104           0 :         struct tdb_record rec;
     105           6 :         char *ret = NULL;
     106           0 :         bool locked;
     107           6 :         size_t unc = 0;
     108           0 :         int len;
     109           0 :         struct tdb_record recovery;
     110             : 
     111             :         /* Read-only databases use no locking at all: it's best-effort.
     112             :          * We may have a write lock already, so skip that case too. */
     113           6 :         if (tdb->read_only || tdb->allrecord_lock.count != 0) {
     114           0 :                 locked = false;
     115             :         } else {
     116           6 :                 if (tdb_lockall_read(tdb) == -1)
     117           0 :                         return NULL;
     118           6 :                 locked = true;
     119             :         }
     120             : 
     121           6 :         if (tdb_recovery_area(tdb, tdb->methods, &rec_off, &recovery) != 0) {
     122           0 :                 goto unlock;
     123             :         }
     124             : 
     125           6 :         tally_init(&freet);
     126           6 :         tally_init(&keys);
     127           6 :         tally_init(&data);
     128           6 :         tally_init(&dead);
     129           6 :         tally_init(&extra);
     130           6 :         tally_init(&hashval);
     131           6 :         tally_init(&uncoal);
     132             : 
     133           6 :         for (off = TDB_DATA_START(tdb->hash_size);
     134        3012 :              off < tdb->map_size - 1;
     135        3006 :              off += sizeof(rec) + rec.rec_len) {
     136        3006 :                 if (tdb->methods->tdb_read(tdb, off, &rec, sizeof(rec),
     137        3006 :                                            DOCONV()) == -1)
     138           0 :                         goto unlock;
     139        3006 :                 switch (rec.magic) {
     140        3000 :                 case TDB_MAGIC:
     141        3000 :                         tally_add(&keys, rec.key_len);
     142        3000 :                         tally_add(&data, rec.data_len);
     143        3000 :                         tally_add(&extra, rec.rec_len - (rec.key_len
     144        3000 :                                                          + rec.data_len));
     145        3000 :                         if (unc > 1)
     146           0 :                                 tally_add(&uncoal, unc - 1);
     147        3000 :                         unc = 0;
     148        3000 :                         break;
     149           6 :                 case TDB_FREE_MAGIC:
     150           6 :                         tally_add(&freet, rec.rec_len);
     151           6 :                         unc++;
     152           6 :                         break;
     153             :                 /* If we crash after ftruncate, we can get zeroes or fill. */
     154           0 :                 case TDB_RECOVERY_INVALID_MAGIC:
     155             :                 case 0x42424242:
     156           0 :                         unc++;
     157             :                         /* If it's a valid recovery, we can trust rec_len. */
     158           0 :                         if (off != rec_off) {
     159           0 :                                 rec.rec_len = tdb_dead_space(tdb, off)
     160           0 :                                         - sizeof(rec);
     161             :                         }
     162             : 
     163           0 :                         FALL_THROUGH;
     164             :                 case TDB_DEAD_MAGIC:
     165           0 :                         tally_add(&dead, rec.rec_len);
     166           0 :                         break;
     167           0 :                 default:
     168           0 :                         TDB_LOG((tdb, TDB_DEBUG_ERROR,
     169             :                                  "Unexpected record magic 0x%x at offset %u\n",
     170             :                                  rec.magic, off));
     171           0 :                         goto unlock;
     172             :                 }
     173             :         }
     174           6 :         if (unc > 1)
     175           0 :                 tally_add(&uncoal, unc - 1);
     176             : 
     177         792 :         for (off = 0; off < tdb->hash_size; off++)
     178         786 :                 tally_add(&hashval, get_hash_length(tdb, off));
     179             : 
     180           6 :         file_size = tdb->hdr_ofs + tdb->map_size;
     181             : 
     182          18 :         len = asprintf(&ret, SUMMARY_FORMAT,
     183           6 :                  (unsigned long long)file_size, keys.total+data.total,
     184           6 :                  (size_t)tdb->hdr_ofs, (size_t)tdb->map_size,
     185             :                  keys.num,
     186           6 :                  (tdb->hash_fn == tdb_jenkins_hash)?"yes":"no",
     187           6 :                  (unsigned)tdb->feature_flags, TDB_SUPPORTED_FEATURE_FLAGS,
     188           6 :                  (tdb->feature_flags & TDB_FEATURE_FLAG_MUTEX)?"yes":"no",
     189             :                  keys.min, tally_mean(&keys), keys.max,
     190             :                  data.min, tally_mean(&data), data.max,
     191             :                  extra.min, tally_mean(&extra), extra.max,
     192             :                  dead.num,
     193             :                  dead.min, tally_mean(&dead), dead.max,
     194             :                  freet.num,
     195             :                  freet.min, tally_mean(&freet), freet.max,
     196             :                  hashval.num,
     197             :                  hashval.min, tally_mean(&hashval), hashval.max,
     198             :                  uncoal.total,
     199             :                  uncoal.min, tally_mean(&uncoal), uncoal.max,
     200           6 :                  keys.total * 100.0 / file_size,
     201           6 :                  data.total * 100.0 / file_size,
     202           6 :                  extra.total * 100.0 / file_size,
     203           6 :                  freet.total * 100.0 / file_size,
     204           6 :                  dead.total * 100.0 / file_size,
     205           6 :                  (keys.num + freet.num + dead.num)
     206           6 :                  * (sizeof(struct tdb_record) + sizeof(uint32_t))
     207           6 :                  * 100.0 / file_size,
     208           6 :                  tdb->hash_size * sizeof(tdb_off_t)
     209           6 :                  * 100.0 / file_size);
     210           6 :         if (len == -1) {
     211           0 :                 goto unlock;
     212             :         }
     213             : 
     214           6 : unlock:
     215           6 :         if (locked) {
     216           6 :                 tdb_unlockall_read(tdb);
     217             :         }
     218           6 :         return ret;
     219             : }

Generated by: LCOV version 1.14