backends/flint/flint_check.cc

Go to the documentation of this file.
00001 /* flint_check.cc: Btree checking
00002  *
00003  * Copyright 1999,2000,2001 BrightStation PLC
00004  * Copyright 2002 Ananova Ltd
00005  * Copyright 2002,2004,2005,2008 Olly Betts
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License as
00009  * published by the Free Software Foundation; either version 2 of the
00010  * License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
00020  * USA
00021  */
00022 
00023 #include <config.h>
00024 
00025 #include <limits.h>
00026 
00027 #include <iostream>
00028 
00029 using namespace std;
00030 
00031 #include "flint_check.h"
00032 
00033 #define REVISION(b)      static_cast<unsigned int>(getint4(b, 0))
00034 #define GET_LEVEL(b)     getint1(b, 4)
00035 #define MAX_FREE(b)      getint2(b, 5)
00036 #define TOTAL_FREE(b)    getint2(b, 7)
00037 #define DIR_END(b)       getint2(b, 9)
00038 #define DIR_START        11
00039 
00040 void BtreeCheck::print_spaces(int n) const
00041 {
00042     while (n--) out.put(' ');
00043 }
00044 
00045 void BtreeCheck::print_bytes(int n, const byte * p) const
00046 {
00047     out.write(reinterpret_cast<const char *>(p), n);
00048 }
00049 
00050 void BtreeCheck::print_key(const byte * p, int c, int j) const
00051 {
00052     Item_ item(p, c);
00053     string key;
00054     if (item.key().length() >= 0)
00055         item.key().read(&key);
00056     if (j == 0) {
00057         out << key << '/' << item.component_of();
00058     } else {
00059         for (string::const_iterator i = key.begin(); i != key.end(); ++i) {
00060             // out << (*i < 32 ? '.' : *i);
00061             char ch = *i;
00062             if (ch < 32) out << '/' << unsigned(ch); else out << ch;
00063         }
00064     }
00065 }
00066 
00067 void BtreeCheck::print_tag(const byte * p, int c, int j) const
00068 {
00069     Item_ item(p, c);
00070     string tag;
00071     item.append_chunk(&tag);
00072     if (j == 0) {
00073         out << "/" << item.components_of() << tag;
00074     } else {
00075         out << "--> [" << getint4(reinterpret_cast<const byte*>(tag.data()), 0)
00076             << ']';
00077     }
00078 }
00079 
00080 void BtreeCheck::report_block_full(int m, int n, const byte * p) const
00081 {
00082     int j = GET_LEVEL(p);
00083     int dir_end = DIR_END(p);
00084     out << '\n';
00085     print_spaces(m);
00086     out << "Block [" << n << "] level " << j << ", revision *" << REVISION(p)
00087          << " items (" << (dir_end - DIR_START)/D2 << ") usage "
00088          << block_usage(p) << "%:\n";
00089     for (int c = DIR_START; c < dir_end; c += D2) {
00090         print_spaces(m);
00091         print_key(p, c, j);
00092         out << ' ';
00093         print_tag(p, c, j);
00094         out << '\n';
00095     }
00096 }
00097 
00098 int BtreeCheck::block_usage(const byte * p) const
00099 {
00100     int space = block_size - DIR_END(p);
00101     int free = TOTAL_FREE(p);
00102     return (space - free) * 100 / space;  /* a percentage */
00103 }
00104 
00108 void BtreeCheck::report_block(int m, int n, const byte * p) const
00109 {
00110     int j = GET_LEVEL(p);
00111     int dir_end = DIR_END(p);
00112     int c;
00113     print_spaces(m);
00114     out << "[" << n << "] *" << REVISION(p) << " ("
00115         << (dir_end - DIR_START)/D2 << ") " << block_usage(p) << "% ";
00116 
00117     for (c = DIR_START; c < dir_end; c += D2) {
00118         if (c == DIR_START + 6) out << "... ";
00119         if (c >= DIR_START + 6 && c < dir_end - 6) continue;
00120 
00121         print_key(p, c, j);
00122         out << ' ';
00123     }
00124     out << endl;
00125 }
00126 
00127 void BtreeCheck::failure(int n) const
00128 {
00129     out << "B-tree error " << n << endl;
00130     throw "btree error";
00131 }
00132 
00133 void
00134 BtreeCheck::block_check(Cursor_ * C_, int j, int opts)
00135 {
00136     byte * p = C_[j].p;
00137     uint4 n = C_[j].n;
00138     size_t c;
00139     size_t significant_c = j == 0 ? DIR_START : DIR_START + D2;
00140         /* the first key in an index block is dummy, remember */
00141 
00142     size_t max_free = MAX_FREE(p);
00143     size_t dir_end = DIR_END(p);
00144     int total_free = block_size - dir_end;
00145 
00146     if (base.block_free_at_start(n)) failure(0);
00147     if (base.block_free_now(n)) failure(1);
00148     base.free_block(n);
00149 
00150     if (j != GET_LEVEL(p)) failure(10);
00151     if (dir_end <= DIR_START || dir_end > block_size) failure(20);
00152 
00153     if (opts & OPT_SHORT_TREE) report_block(3*(level - j), n, p);
00154 
00155     if (opts & OPT_FULL_TREE) report_block_full(3*(level - j), n, p);
00156 
00157     for (c = DIR_START; c < dir_end; c += D2) {
00158         Item_ item(p, c);
00159         int o = item.get_address() - p;
00160         if (o > int(block_size)) failure(21);
00161         if (o - dir_end < max_free) failure(30);
00162 
00163         int kt_len = item.size();
00164         if (o + kt_len > int(block_size)) failure(40);
00165         total_free -= kt_len;
00166 
00167         if (c > significant_c && Item_(p, c - D2).key() >= item.key())
00168             failure(50);
00169     }
00170     if (total_free != TOTAL_FREE(p))
00171         failure(60);
00172 
00173     if (j == 0) return;
00174     for (c = DIR_START; c < dir_end; c += D2) {
00175         C_[j].c = c;
00176         block_to_cursor(C_, j - 1, Item_(p, c).block_given_by());
00177 
00178         block_check(C_, j - 1, opts);
00179 
00180         byte * q = C_[j - 1].p;
00181         /* if j == 1, and c > DIR_START, the first key of level j - 1 must be
00182          * >= the key of p, c: */
00183 
00184         if (j == 1 && c > DIR_START)
00185             if (Item_(q, DIR_START).key() < Item_(p, c).key())
00186                 failure(70);
00187 
00188         /* if j > 1, and c > DIR_START, the second key of level j - 1 must be
00189          * >= the key of p, c: */
00190 
00191         if (j > 1 && c > DIR_START && DIR_END(q) > DIR_START + D2 &&
00192             Item_(q, DIR_START + D2).key() < Item_(p, c).key())
00193             failure(80);
00194 
00195         /* the last key of level j - 1 must be < the key of p, c + D2, if c +
00196          * D2 < dir_end: */
00197 
00198         if (c + D2 < dir_end &&
00199             (j == 1 || DIR_START + D2 < DIR_END(q)) &&
00200             Item_(q, DIR_END(q) - D2).key() >= Item_(p, c + D2).key())
00201             failure(90);
00202 
00203         if (REVISION(q) > REVISION(p)) failure(91);
00204     }
00205 }
00206 
00207 void
00208 BtreeCheck::check(const string & name, int opts, ostream &out)
00209 {
00210     BtreeCheck B(name, false, out);
00211     B.open(); // throws exception if open fails
00212     Cursor_ * C = B.C;
00213 
00214     if (opts & OPT_SHOW_STATS) {
00215         out << "base" << (char)B.base_letter
00216             << " blocksize=" << B.block_size / 1024 << "K"
00217                " items=" << B.item_count
00218             << " lastblock=" << B.base.get_last_block()
00219             << " revision=" << B.revision_number
00220             << " levels=" << B.level
00221             << " root=";
00222         if (B.faked_root_block)
00223             out << "(faked)";
00224         else
00225             out << C[B.level].n;
00226         out << endl;
00227     }
00228 
00229     int limit = B.base.get_bit_map_size() - 1;
00230 
00231     limit = limit * CHAR_BIT + CHAR_BIT - 1;
00232 
00233     if (opts & OPT_SHOW_BITMAP) {
00234         for (int j = 0; j <= limit; j++) {
00235             out << (B.base.block_free_at_start(j) ? '.' : '*');
00236             if (j > 0) {
00237                 if ((j + 1) % 100 == 0) {
00238                     out << '\n';
00239                 } else if ((j + 1) % 10 == 0) {
00240                     out << ' ';
00241                 }
00242             }
00243         }
00244         out << '\n' << endl;
00245     }
00246 
00247     if (B.faked_root_block) {
00248         if (opts) out << "void ";
00249     } else {
00250         B.block_check(C, B.level, opts);
00251 
00252         /* the bit map should now be entirely clear: */
00253 
00254         if (!B.base.is_empty()) {
00255             B.failure(100);
00256         }
00257     }
00258     if (opts) out << "B-tree checked okay" << endl;
00259 }
00260 
00261 void BtreeCheck::report_cursor(int N, const Cursor_ * C_) const
00262 {
00263     out << N << ")\n";
00264     for (int i = 0; i <= level; i++)
00265         out << "p=" << C_[i].p << ", c=" << C_[i].c << ", n=[" << C_[i].n
00266             << "], rewrite=" << C_[i].rewrite << endl;
00267 }

Documentation for Xapian (version 1.0.10).
Generated on 24 Dec 2008 by Doxygen 1.5.2.