00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <config.h>
00025
00026 #include "flint_check.h"
00027
00028 #include <climits>
00029
00030 using namespace std;
00031
00032 #define REVISION(b) static_cast<unsigned int>(getint4(b, 0))
00033 #define GET_LEVEL(b) getint1(b, 4)
00034 #define MAX_FREE(b) getint2(b, 5)
00035 #define TOTAL_FREE(b) getint2(b, 7)
00036 #define DIR_END(b) getint2(b, 9)
00037 #define DIR_START 11
00038
00039 void BtreeCheck::print_spaces(int n) const
00040 {
00041 while (n--) out.put(' ');
00042 }
00043
00044 void BtreeCheck::print_bytes(int n, const byte * p) const
00045 {
00046 out.write(reinterpret_cast<const char *>(p), n);
00047 }
00048
00049 void BtreeCheck::print_key(const byte * p, int c, int j) const
00050 {
00051 Item_ item(p, c);
00052 string key;
00053 if (item.key().length() >= 0)
00054 item.key().read(&key);
00055 if (j == 0) {
00056 out << key << '/' << item.component_of();
00057 } else {
00058 for (string::const_iterator i = key.begin(); i != key.end(); ++i) {
00059
00060 char ch = *i;
00061 if (ch < 32) out << '/' << unsigned(ch); else out << ch;
00062 }
00063 }
00064 }
00065
00066 void BtreeCheck::print_tag(const byte * p, int c, int j) const
00067 {
00068 Item_ item(p, c);
00069 string tag;
00070 item.append_chunk(&tag);
00071 if (j == 0) {
00072 out << "/" << item.components_of() << tag;
00073 } else {
00074 out << "--> [" << getint4(reinterpret_cast<const byte*>(tag.data()), 0)
00075 << ']';
00076 }
00077 }
00078
00079 void BtreeCheck::report_block_full(int m, int n, const byte * p) const
00080 {
00081 int j = GET_LEVEL(p);
00082 int dir_end = DIR_END(p);
00083 out << '\n';
00084 print_spaces(m);
00085 out << "Block [" << n << "] level " << j << ", revision *" << REVISION(p)
00086 << " items (" << (dir_end - DIR_START)/D2 << ") usage "
00087 << block_usage(p) << "%:\n";
00088 for (int c = DIR_START; c < dir_end; c += D2) {
00089 print_spaces(m);
00090 print_key(p, c, j);
00091 out << ' ';
00092 print_tag(p, c, j);
00093 out << '\n';
00094 }
00095 }
00096
00097 int BtreeCheck::block_usage(const byte * p) const
00098 {
00099 int space = block_size - DIR_END(p);
00100 int free = TOTAL_FREE(p);
00101 return (space - free) * 100 / space;
00102 }
00103
00107 void BtreeCheck::report_block(int m, int n, const byte * p) const
00108 {
00109 int j = GET_LEVEL(p);
00110 int dir_end = DIR_END(p);
00111 int c;
00112 print_spaces(m);
00113 out << "[" << n << "] *" << REVISION(p) << " ("
00114 << (dir_end - DIR_START)/D2 << ") " << block_usage(p) << "% ";
00115
00116 for (c = DIR_START; c < dir_end; c += D2) {
00117 if (c == DIR_START + 6) out << "... ";
00118 if (c >= DIR_START + 6 && c < dir_end - 6) continue;
00119
00120 print_key(p, c, j);
00121 out << ' ';
00122 }
00123 out << endl;
00124 }
00125
00126 void BtreeCheck::failure(int n) const
00127 {
00128 out << "B-tree error " << n << endl;
00129 throw "btree error";
00130 }
00131
00132 void
00133 BtreeCheck::block_check(Cursor_ * C_, int j, int opts)
00134 {
00135 byte * p = C_[j].p;
00136 uint4 n = C_[j].n;
00137 size_t c;
00138 size_t significant_c = j == 0 ? DIR_START : DIR_START + D2;
00139
00140
00141 size_t max_free = MAX_FREE(p);
00142 size_t dir_end = DIR_END(p);
00143 int total_free = block_size - dir_end;
00144
00145 if (base.block_free_at_start(n)) failure(0);
00146 if (base.block_free_now(n)) failure(1);
00147 base.free_block(n);
00148
00149 if (j != GET_LEVEL(p)) failure(10);
00150 if (dir_end <= DIR_START || dir_end > block_size) failure(20);
00151
00152 if (opts & OPT_SHORT_TREE) report_block(3*(level - j), n, p);
00153
00154 if (opts & OPT_FULL_TREE) report_block_full(3*(level - j), n, p);
00155
00156 for (c = DIR_START; c < dir_end; c += D2) {
00157 Item_ item(p, c);
00158 int o = item.get_address() - p;
00159 if (o > int(block_size)) failure(21);
00160 if (o - dir_end < max_free) failure(30);
00161
00162 int kt_len = item.size();
00163 if (o + kt_len > int(block_size)) failure(40);
00164 total_free -= kt_len;
00165
00166 if (c > significant_c && Item_(p, c - D2).key() >= item.key())
00167 failure(50);
00168 }
00169 if (total_free != TOTAL_FREE(p))
00170 failure(60);
00171
00172 if (j == 0) return;
00173 for (c = DIR_START; c < dir_end; c += D2) {
00174 C_[j].c = c;
00175 block_to_cursor(C_, j - 1, Item_(p, c).block_given_by());
00176
00177 block_check(C_, j - 1, opts);
00178
00179 byte * q = C_[j - 1].p;
00180
00181
00182
00183 if (j == 1 && c > DIR_START)
00184 if (Item_(q, DIR_START).key() < Item_(p, c).key())
00185 failure(70);
00186
00187
00188
00189
00190 if (j > 1 && c > DIR_START && DIR_END(q) > DIR_START + D2 &&
00191 Item_(q, DIR_START + D2).key() < Item_(p, c).key())
00192 failure(80);
00193
00194
00195
00196
00197 if (c + D2 < dir_end &&
00198 (j == 1 || DIR_START + D2 < DIR_END(q)) &&
00199 Item_(q, DIR_END(q) - D2).key() >= Item_(p, c + D2).key())
00200 failure(90);
00201
00202 if (REVISION(q) > REVISION(p)) failure(91);
00203 }
00204 }
00205
00206 void
00207 BtreeCheck::check(const char * tablename, const string & path, int opts,
00208 ostream &out)
00209 {
00210 BtreeCheck B(tablename, path, false, out);
00211 B.open();
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
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 }