LCOV - code coverage report
Current view: top level - backends/flint - flint_check.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core r Lines: 44 133 33.1 %
Date: 2011-08-21 Functions: 4 13 30.8 %
Branches: 27 116 23.3 %

           Branch data     Line data    Source code
       1                 :            : /* flint_check.cc: Btree checking
       2                 :            :  *
       3                 :            :  * Copyright 1999,2000,2001 BrightStation PLC
       4                 :            :  * Copyright 2002 Ananova Ltd
       5                 :            :  * Copyright 2002,2004,2005,2008 Olly Betts
       6                 :            :  * Copyright 2008 Lemur Consulting Ltd
       7                 :            :  *
       8                 :            :  * This program is free software; you can redistribute it and/or
       9                 :            :  * modify it under the terms of the GNU General Public License as
      10                 :            :  * published by the Free Software Foundation; either version 2 of the
      11                 :            :  * License, or (at your option) any later version.
      12                 :            :  *
      13                 :            :  * This program is distributed in the hope that it will be useful,
      14                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      15                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16                 :            :  * GNU General Public License for more details.
      17                 :            :  *
      18                 :            :  * You should have received a copy of the GNU General Public License
      19                 :            :  * along with this program; if not, write to the Free Software
      20                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
      21                 :            :  * USA
      22                 :            :  */
      23                 :            : 
      24                 :            : #include <config.h>
      25                 :            : 
      26                 :            : #include "flint_check.h"
      27                 :            : 
      28                 :            : #include <climits>
      29                 :            : 
      30                 :            : using namespace std;
      31                 :            : 
      32                 :            : #define REVISION(b)      static_cast<unsigned int>(getint4(b, 0))
      33                 :            : #define GET_LEVEL(b)     getint1(b, 4)
      34                 :            : #define MAX_FREE(b)      getint2(b, 5)
      35                 :            : #define TOTAL_FREE(b)    getint2(b, 7)
      36                 :            : #define DIR_END(b)       getint2(b, 9)
      37                 :            : #define DIR_START        11
      38                 :            : 
      39                 :          0 : void BtreeCheck::print_spaces(int n) const
      40                 :            : {
      41         [ #  # ]:          0 :     while (n--) out.put(' ');
      42                 :          0 : }
      43                 :            : 
      44                 :          0 : void BtreeCheck::print_bytes(int n, const byte * p) const
      45                 :            : {
      46                 :          0 :     out.write(reinterpret_cast<const char *>(p), n);
      47                 :          0 : }
      48                 :            : 
      49                 :          0 : void BtreeCheck::print_key(const byte * p, int c, int j) const
      50                 :            : {
      51                 :          0 :     Item_ item(p, c);
      52                 :          0 :     string key;
      53         [ #  # ]:          0 :     if (item.key().length() >= 0)
      54                 :          0 :         item.key().read(&key);
      55         [ #  # ]:          0 :     if (j == 0) {
      56                 :          0 :         out << key << '/' << item.component_of();
      57                 :            :     } else {
      58         [ #  # ]:          0 :         for (string::const_iterator i = key.begin(); i != key.end(); ++i) {
      59                 :            :             // out << (*i < 32 ? '.' : *i);
      60                 :          0 :             char ch = *i;
      61         [ #  # ]:          0 :             if (ch < 32) out << '/' << unsigned(ch); else out << ch;
      62                 :            :         }
      63                 :          0 :     }
      64                 :          0 : }
      65                 :            : 
      66                 :          0 : void BtreeCheck::print_tag(const byte * p, int c, int j) const
      67                 :            : {
      68                 :          0 :     Item_ item(p, c);
      69                 :          0 :     string tag;
      70                 :          0 :     item.append_chunk(&tag);
      71         [ #  # ]:          0 :     if (j == 0) {
      72                 :          0 :         out << "/" << item.components_of() << tag;
      73                 :            :     } else {
      74                 :            :         out << "--> [" << getint4(reinterpret_cast<const byte*>(tag.data()), 0)
      75                 :          0 :             << ']';
      76                 :          0 :     }
      77                 :          0 : }
      78                 :            : 
      79                 :          0 : void BtreeCheck::report_block_full(int m, int n, const byte * p) const
      80                 :            : {
      81                 :          0 :     int j = GET_LEVEL(p);
      82                 :          0 :     int dir_end = DIR_END(p);
      83                 :          0 :     out << '\n';
      84                 :          0 :     print_spaces(m);
      85                 :            :     out << "Block [" << n << "] level " << j << ", revision *" << REVISION(p)
      86                 :            :          << " items (" << (dir_end - DIR_START)/D2 << ") usage "
      87                 :          0 :          << block_usage(p) << "%:\n";
      88         [ #  # ]:          0 :     for (int c = DIR_START; c < dir_end; c += D2) {
      89                 :          0 :         print_spaces(m);
      90                 :          0 :         print_key(p, c, j);
      91                 :          0 :         out << ' ';
      92                 :          0 :         print_tag(p, c, j);
      93                 :          0 :         out << '\n';
      94                 :            :     }
      95                 :          0 : }
      96                 :            : 
      97                 :          0 : int BtreeCheck::block_usage(const byte * p) const
      98                 :            : {
      99                 :          0 :     int space = block_size - DIR_END(p);
     100                 :          0 :     int free = TOTAL_FREE(p);
     101                 :          0 :     return (space - free) * 100 / space;  /* a percentage */
     102                 :            : }
     103                 :            : 
     104                 :            : /** BtreeCheck::report_block(m, n, p) prints the block at p, block number n,
     105                 :            :  *  indented by m spaces.
     106                 :            :  */
     107                 :          0 : void BtreeCheck::report_block(int m, int n, const byte * p) const
     108                 :            : {
     109                 :          0 :     int j = GET_LEVEL(p);
     110                 :          0 :     int dir_end = DIR_END(p);
     111                 :            :     int c;
     112                 :          0 :     print_spaces(m);
     113                 :            :     out << "[" << n << "] *" << REVISION(p) << " ("
     114                 :          0 :         << (dir_end - DIR_START)/D2 << ") " << block_usage(p) << "% ";
     115                 :            : 
     116         [ #  # ]:          0 :     for (c = DIR_START; c < dir_end; c += D2) {
     117         [ #  # ]:          0 :         if (c == DIR_START + 6) out << "... ";
     118 [ #  # ][ #  # ]:          0 :         if (c >= DIR_START + 6 && c < dir_end - 6) continue;
     119                 :            : 
     120                 :          0 :         print_key(p, c, j);
     121                 :          0 :         out << ' ';
     122                 :            :     }
     123                 :          0 :     out << endl;
     124                 :          0 : }
     125                 :            : 
     126                 :          0 : void BtreeCheck::failure(int n) const
     127                 :            : {
     128                 :          0 :     out << "B-tree error " << n << endl;
     129                 :          0 :     throw "btree error";
     130                 :            : }
     131                 :            : 
     132                 :            : void
     133                 :          4 : BtreeCheck::block_check(Cursor_ * C_, int j, int opts)
     134                 :            : {
     135                 :          4 :     byte * p = C_[j].p;
     136                 :          4 :     uint4 n = C_[j].n;
     137                 :            :     size_t c;
     138         [ +  - ]:          4 :     size_t significant_c = j == 0 ? DIR_START : DIR_START + D2;
     139                 :            :         /* the first key in an index block is dummy, remember */
     140                 :            : 
     141                 :          4 :     size_t max_free = MAX_FREE(p);
     142                 :          4 :     size_t dir_end = DIR_END(p);
     143                 :          4 :     int total_free = block_size - dir_end;
     144                 :            : 
     145         [ -  + ]:          4 :     if (base.block_free_at_start(n)) failure(0);
     146         [ -  + ]:          4 :     if (base.block_free_now(n)) failure(1);
     147                 :          4 :     base.free_block(n);
     148                 :            : 
     149         [ -  + ]:          4 :     if (j != GET_LEVEL(p)) failure(10);
     150 [ +  - ][ -  + ]:          4 :     if (dir_end <= DIR_START || dir_end > block_size) failure(20);
     151                 :            : 
     152         [ -  + ]:          4 :     if (opts & OPT_SHORT_TREE) report_block(3*(level - j), n, p);
     153                 :            : 
     154         [ -  + ]:          4 :     if (opts & OPT_FULL_TREE) report_block_full(3*(level - j), n, p);
     155                 :            : 
     156         [ +  + ]:          9 :     for (c = DIR_START; c < dir_end; c += D2) {
     157                 :          5 :         Item_ item(p, c);
     158                 :          5 :         int o = item.get_address() - p;
     159         [ -  + ]:          5 :         if (o > int(block_size)) failure(21);
     160         [ -  + ]:          5 :         if (o - dir_end < max_free) failure(30);
     161                 :            : 
     162                 :          5 :         int kt_len = item.size();
     163         [ -  + ]:          5 :         if (o + kt_len > int(block_size)) failure(40);
     164                 :          5 :         total_free -= kt_len;
     165                 :            : 
     166 [ +  + ][ -  + ]:          5 :         if (c > significant_c && Item_(p, c - D2).key() >= item.key())
                 [ -  + ]
     167                 :          0 :             failure(50);
     168                 :            :     }
     169         [ -  + ]:          4 :     if (total_free != TOTAL_FREE(p))
     170                 :          0 :         failure(60);
     171                 :            : 
     172         [ +  - ]:          4 :     if (j == 0) return;
     173         [ #  # ]:          4 :     for (c = DIR_START; c < dir_end; c += D2) {
     174                 :          0 :         C_[j].c = c;
     175                 :          0 :         block_to_cursor(C_, j - 1, Item_(p, c).block_given_by());
     176                 :            : 
     177                 :          0 :         block_check(C_, j - 1, opts);
     178                 :            : 
     179                 :          0 :         byte * q = C_[j - 1].p;
     180                 :            :         /* if j == 1, and c > DIR_START, the first key of level j - 1 must be
     181                 :            :          * >= the key of p, c: */
     182                 :            : 
     183   [ #  #  #  # ]:          0 :         if (j == 1 && c > DIR_START)
     184         [ #  # ]:          0 :             if (Item_(q, DIR_START).key() < Item_(p, c).key())
     185                 :          0 :                 failure(70);
     186                 :            : 
     187                 :            :         /* if j > 1, and c > DIR_START, the second key of level j - 1 must be
     188                 :            :          * >= the key of p, c: */
     189                 :            : 
     190 [ #  # ][ #  # ]:          0 :         if (j > 1 && c > DIR_START && DIR_END(q) > DIR_START + D2 &&
         [ #  # ][ #  # ]
                 [ #  # ]
     191                 :            :             Item_(q, DIR_START + D2).key() < Item_(p, c).key())
     192                 :          0 :             failure(80);
     193                 :            : 
     194                 :            :         /* the last key of level j - 1 must be < the key of p, c + D2, if c +
     195                 :            :          * D2 < dir_end: */
     196                 :            : 
     197 [ #  # ][ #  # ]:          0 :         if (c + D2 < dir_end &&
         [ #  # ][ #  # ]
                 [ #  # ]
     198                 :            :             (j == 1 || DIR_START + D2 < DIR_END(q)) &&
     199                 :            :             Item_(q, DIR_END(q) - D2).key() >= Item_(p, c + D2).key())
     200                 :          0 :             failure(90);
     201                 :            : 
     202         [ #  # ]:          0 :         if (REVISION(q) > REVISION(p)) failure(91);
     203                 :            :     }
     204                 :            : }
     205                 :            : 
     206                 :            : void
     207                 :          4 : BtreeCheck::check(const char * tablename, const string & path, int opts,
     208                 :            :                   ostream &out)
     209                 :            : {
     210                 :          4 :     BtreeCheck B(tablename, path, false, out);
     211                 :          4 :     B.open(); // throws exception if open fails
     212                 :          4 :     Cursor_ * C = B.C;
     213                 :            : 
     214         [ +  - ]:          4 :     if (opts & OPT_SHOW_STATS) {
     215                 :            :         out << "base" << (char)B.base_letter
     216                 :            :             << " blocksize=" << B.block_size / 1024 << "K"
     217                 :            :                " items=" << B.item_count
     218                 :            :             << " lastblock=" << B.base.get_last_block()
     219                 :            :             << " revision=" << B.revision_number
     220                 :            :             << " levels=" << B.level
     221                 :          4 :             << " root=";
     222         [ -  + ]:          4 :         if (B.faked_root_block)
     223                 :          0 :             out << "(faked)";
     224                 :            :         else
     225                 :          4 :             out << C[B.level].n;
     226                 :          4 :         out << endl;
     227                 :            :     }
     228                 :            : 
     229                 :          4 :     int limit = B.base.get_bit_map_size() - 1;
     230                 :            : 
     231                 :          4 :     limit = limit * CHAR_BIT + CHAR_BIT - 1;
     232                 :            : 
     233         [ -  + ]:          4 :     if (opts & OPT_SHOW_BITMAP) {
     234         [ #  # ]:          0 :         for (int j = 0; j <= limit; j++) {
     235         [ #  # ]:          0 :             out << (B.base.block_free_at_start(j) ? '.' : '*');
     236         [ #  # ]:          0 :             if (j > 0) {
     237         [ #  # ]:          0 :                 if ((j + 1) % 100 == 0) {
     238                 :          0 :                     out << '\n';
     239         [ #  # ]:          0 :                 } else if ((j + 1) % 10 == 0) {
     240                 :          0 :                     out << ' ';
     241                 :            :                 }
     242                 :            :             }
     243                 :            :         }
     244                 :          0 :         out << '\n' << endl;
     245                 :            :     }
     246                 :            : 
     247         [ -  + ]:          4 :     if (B.faked_root_block) {
     248         [ #  # ]:          0 :         if (opts) out << "void ";
     249                 :            :     } else {
     250                 :          4 :         B.block_check(C, B.level, opts);
     251                 :            : 
     252                 :            :         /* the bit map should now be entirely clear: */
     253                 :            : 
     254         [ -  + ]:          4 :         if (!B.base.is_empty()) {
     255                 :          0 :             B.failure(100);
     256                 :            :         }
     257                 :            :     }
     258         [ +  - ]:          4 :     if (opts) out << "B-tree checked okay" << endl;
     259                 :          4 : }
     260                 :            : 
     261                 :          0 : void BtreeCheck::report_cursor(int N, const Cursor_ * C_) const
     262                 :            : {
     263                 :          0 :     out << N << ")\n";
     264         [ #  # ]:          0 :     for (int i = 0; i <= level; i++)
     265                 :          0 :         out << "p=" << C_[i].p << ", c=" << C_[i].c << ", n=[" << C_[i].n
     266                 :          0 :             << "], rewrite=" << C_[i].rewrite << endl;
     267 [ +  - ][ +  - ]:         15 : }

Generated by: LCOV version 1.8