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 "chert_check.h"
00027
00028 #include <climits>
00029
00030 using namespace std;
00031
00032 void ChertTableCheck::print_spaces(int n) const
00033 {
00034 while (n--) out.put(' ');
00035 }
00036
00037 void ChertTableCheck::print_bytes(int n, const byte * p) const
00038 {
00039 out.write(reinterpret_cast<const char *>(p), n);
00040 }
00041
00042 void ChertTableCheck::print_key(const byte * p, int c, int j) const
00043 {
00044 Item item(p, c);
00045 string key;
00046 if (item.key().length() >= 0)
00047 item.key().read(&key);
00048 if (j == 0) {
00049 out << key << '/' << item.component_of();
00050 } else {
00051 for (string::const_iterator i = key.begin(); i != key.end(); ++i) {
00052
00053 char ch = *i;
00054 if (ch < 32) out << '/' << unsigned(ch); else out << ch;
00055 }
00056 }
00057 }
00058
00059 void ChertTableCheck::print_tag(const byte * p, int c, int j) const
00060 {
00061 Item item(p, c);
00062 string tag;
00063 item.append_chunk(&tag);
00064 if (j == 0) {
00065 out << "/" << item.components_of() << tag;
00066 } else {
00067 out << "--> [" << getint4(reinterpret_cast<const byte*>(tag.data()), 0)
00068 << ']';
00069 }
00070 }
00071
00072 void ChertTableCheck::report_block_full(int m, int n, const byte * p) const
00073 {
00074 int j = GET_LEVEL(p);
00075 int dir_end = DIR_END(p);
00076 out << '\n';
00077 print_spaces(m);
00078 out << "Block [" << n << "] level " << j << ", revision *" << REVISION(p)
00079 << " items (" << (dir_end - DIR_START)/D2 << ") usage "
00080 << block_usage(p) << "%:\n";
00081 for (int c = DIR_START; c < dir_end; c += D2) {
00082 print_spaces(m);
00083 print_key(p, c, j);
00084 out << ' ';
00085 print_tag(p, c, j);
00086 out << '\n';
00087 }
00088 }
00089
00090 int ChertTableCheck::block_usage(const byte * p) const
00091 {
00092 int space = block_size - DIR_END(p);
00093 int free = TOTAL_FREE(p);
00094 return (space - free) * 100 / space;
00095 }
00096
00100 void ChertTableCheck::report_block(int m, int n, const byte * p) const
00101 {
00102 int j = GET_LEVEL(p);
00103 int dir_end = DIR_END(p);
00104 int c;
00105 print_spaces(m);
00106 out << "[" << n << "] *" << REVISION(p) << " ("
00107 << (dir_end - DIR_START)/D2 << ") " << block_usage(p) << "% ";
00108
00109 for (c = DIR_START; c < dir_end; c += D2) {
00110 if (c == DIR_START + 6) out << "... ";
00111 if (c >= DIR_START + 6 && c < dir_end - 6) continue;
00112
00113 print_key(p, c, j);
00114 out << ' ';
00115 }
00116 out << endl;
00117 }
00118
00119 void ChertTableCheck::failure(int n) const
00120 {
00121 out << "B-tree error " << n << endl;
00122 throw "btree error";
00123 }
00124
00125 void
00126 ChertTableCheck::block_check(Cursor * C_, int j, int opts)
00127 {
00128 byte * p = C_[j].p;
00129 uint4 n = C_[j].n;
00130 size_t c;
00131 size_t significant_c = j == 0 ? DIR_START : DIR_START + D2;
00132
00133
00134 size_t max_free = MAX_FREE(p);
00135 size_t dir_end = DIR_END(p);
00136 int total_free = block_size - dir_end;
00137
00138 if (base.block_free_at_start(n)) failure(0);
00139 if (base.block_free_now(n)) failure(1);
00140 base.free_block(n);
00141
00142 if (j != GET_LEVEL(p)) failure(10);
00143 if (dir_end <= DIR_START || dir_end > block_size) failure(20);
00144
00145 if (opts & OPT_SHORT_TREE) report_block(3*(level - j), n, p);
00146
00147 if (opts & OPT_FULL_TREE) report_block_full(3*(level - j), n, p);
00148
00149 for (c = DIR_START; c < dir_end; c += D2) {
00150 Item item(p, c);
00151 int o = item.get_address() - p;
00152 if (o > int(block_size)) failure(21);
00153 if (o - dir_end < max_free) failure(30);
00154
00155 int kt_len = item.size();
00156 if (o + kt_len > int(block_size)) failure(40);
00157 total_free -= kt_len;
00158
00159 if (c > significant_c && Item(p, c - D2).key() >= item.key())
00160 failure(50);
00161 }
00162 if (total_free != TOTAL_FREE(p))
00163 failure(60);
00164
00165 if (j == 0) return;
00166 for (c = DIR_START; c < dir_end; c += D2) {
00167 C_[j].c = c;
00168 block_to_cursor(C_, j - 1, Item(p, c).block_given_by());
00169
00170 block_check(C_, j - 1, opts);
00171
00172 byte * q = C_[j - 1].p;
00173
00174
00175
00176 if (j == 1 && c > DIR_START)
00177 if (Item(q, DIR_START).key() < Item(p, c).key())
00178 failure(70);
00179
00180
00181
00182
00183 if (j > 1 && c > DIR_START && DIR_END(q) > DIR_START + D2 &&
00184 Item(q, DIR_START + D2).key() < Item(p, c).key())
00185 failure(80);
00186
00187
00188
00189
00190 if (c + D2 < dir_end &&
00191 (j == 1 || DIR_START + D2 < DIR_END(q)) &&
00192 Item(q, DIR_END(q) - D2).key() >= Item(p, c + D2).key())
00193 failure(90);
00194
00195 if (REVISION(q) > REVISION(p)) failure(91);
00196 }
00197 }
00198
00199 void
00200 ChertTableCheck::check(const char * tablename, const string & path, int opts,
00201 ostream &out)
00202 {
00203 ChertTableCheck B(tablename, path, false, out);
00204 B.open();
00205 Cursor * C = B.C;
00206
00207 if (opts & OPT_SHOW_STATS) {
00208 out << "base" << (char)B.base_letter
00209 << " blocksize=" << B.block_size / 1024 << "K"
00210 " items=" << B.item_count
00211 << " lastblock=" << B.base.get_last_block()
00212 << " revision=" << B.revision_number
00213 << " levels=" << B.level
00214 << " root=";
00215 if (B.faked_root_block)
00216 out << "(faked)";
00217 else
00218 out << C[B.level].n;
00219 out << endl;
00220 }
00221
00222 int limit = B.base.get_bit_map_size() - 1;
00223
00224 limit = limit * CHAR_BIT + CHAR_BIT - 1;
00225
00226 if (opts & OPT_SHOW_BITMAP) {
00227 for (int j = 0; j <= limit; j++) {
00228 out << (B.base.block_free_at_start(j) ? '.' : '*');
00229 if (j > 0) {
00230 if ((j + 1) % 100 == 0) {
00231 out << '\n';
00232 } else if ((j + 1) % 10 == 0) {
00233 out << ' ';
00234 }
00235 }
00236 }
00237 out << '\n' << endl;
00238 }
00239
00240 if (B.faked_root_block) {
00241 if (opts) out << "void ";
00242 } else {
00243 B.block_check(C, B.level, opts);
00244
00245
00246
00247 if (!B.base.is_empty()) {
00248 B.failure(100);
00249 }
00250 }
00251 if (opts) out << "B-tree checked okay" << endl;
00252 }
00253
00254 void ChertTableCheck::report_cursor(int N, const Cursor * C_) const
00255 {
00256 out << N << ")\n";
00257 for (int i = 0; i <= level; i++)
00258 out << "p=" << C_[i].p << ", c=" << C_[i].c << ", n=[" << C_[i].n
00259 << "], rewrite=" << C_[i].rewrite << endl;
00260 }