| 1 | |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | |
|---|
| 5 | |
|---|
| 6 | |
|---|
| 7 | |
|---|
| 8 | |
|---|
| 9 | |
|---|
| 10 | |
|---|
| 11 | |
|---|
| 12 | |
|---|
| 13 | |
|---|
| 14 | |
|---|
| 15 | |
|---|
| 16 | |
|---|
| 17 | |
|---|
| 18 | |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | |
|---|
| 22 | |
|---|
| 23 | #include <config.h> |
|---|
| 24 | |
|---|
| 25 | #include <limits.h> |
|---|
| 26 | |
|---|
| 27 | #include <iostream> |
|---|
| 28 | |
|---|
| 29 | using namespace std; |
|---|
| 30 | |
|---|
| 31 | #include "flint_check.h" |
|---|
| 32 | |
|---|
| 33 | #define REVISION(b) static_cast<unsigned int>(getint4(b, 0)) |
|---|
| 34 | #define GET_LEVEL(b) getint1(b, 4) |
|---|
| 35 | #define MAX_FREE(b) getint2(b, 5) |
|---|
| 36 | #define TOTAL_FREE(b) getint2(b, 7) |
|---|
| 37 | #define DIR_END(b) getint2(b, 9) |
|---|
| 38 | #define DIR_START 11 |
|---|
| 39 | |
|---|
| 40 | void BtreeCheck::print_spaces(int n) const |
|---|
| 41 | { |
|---|
| 42 | while (n--) out.put(' '); |
|---|
| 43 | } |
|---|
| 44 | |
|---|
| 45 | void BtreeCheck::print_bytes(int n, const byte * p) const |
|---|
| 46 | { |
|---|
| 47 | out.write(reinterpret_cast<const char *>(p), n); |
|---|
| 48 | } |
|---|
| 49 | |
|---|
| 50 | void BtreeCheck::print_key(const byte * p, int c, int j) const |
|---|
| 51 | { |
|---|
| 52 | Item_ item(p, c); |
|---|
| 53 | string key; |
|---|
| 54 | if (item.key().length() >= 0) |
|---|
| 55 | item.key().read(&key); |
|---|
| 56 | if (j == 0) { |
|---|
| 57 | out << key << '/' << item.component_of(); |
|---|
| 58 | } else { |
|---|
| 59 | for (string::const_iterator i = key.begin(); i != key.end(); ++i) { |
|---|
| 60 | |
|---|
| 61 | char ch = *i; |
|---|
| 62 | if (ch < 32) out << '/' << unsigned(ch); else out << ch; |
|---|
| 63 | } |
|---|
| 64 | } |
|---|
| 65 | } |
|---|
| 66 | |
|---|
| 67 | void BtreeCheck::print_tag(const byte * p, int c, int j) const |
|---|
| 68 | { |
|---|
| 69 | Item_ item(p, c); |
|---|
| 70 | string tag; |
|---|
| 71 | item.append_chunk(&tag); |
|---|
| 72 | if (j == 0) { |
|---|
| 73 | out << "/" << item.components_of() << tag; |
|---|
| 74 | } else { |
|---|
| 75 | out << "--> [" << getint4(reinterpret_cast<const byte*>(tag.data()), 0) |
|---|
| 76 | << ']'; |
|---|
| 77 | } |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | void BtreeCheck::report_block_full(int m, int n, const byte * p) const |
|---|
| 81 | { |
|---|
| 82 | int j = GET_LEVEL(p); |
|---|
| 83 | int dir_end = DIR_END(p); |
|---|
| 84 | out << '\n'; |
|---|
| 85 | print_spaces(m); |
|---|
| 86 | out << "Block [" << n << "] level " << j << ", revision *" << REVISION(p) |
|---|
| 87 | << " items (" << (dir_end - DIR_START)/D2 << ") usage " |
|---|
| 88 | << block_usage(p) << "%:\n"; |
|---|
| 89 | for (int c = DIR_START; c < dir_end; c += D2) { |
|---|
| 90 | print_spaces(m); |
|---|
| 91 | print_key(p, c, j); |
|---|
| 92 | out << ' '; |
|---|
| 93 | print_tag(p, c, j); |
|---|
| 94 | out << '\n'; |
|---|
| 95 | } |
|---|
| 96 | } |
|---|
| 97 | |
|---|
| 98 | int BtreeCheck::block_usage(const byte * p) const |
|---|
| 99 | { |
|---|
| 100 | int space = block_size - DIR_END(p); |
|---|
| 101 | int free = TOTAL_FREE(p); |
|---|
| 102 | return (space - free) * 100 / space; |
|---|
| 103 | } |
|---|
| 104 | |
|---|
| 105 | |
|---|
| 106 | |
|---|
| 107 | |
|---|
| 108 | void BtreeCheck::report_block(int m, int n, const byte * p) const |
|---|
| 109 | { |
|---|
| 110 | int j = GET_LEVEL(p); |
|---|
| 111 | int dir_end = DIR_END(p); |
|---|
| 112 | int c; |
|---|
| 113 | print_spaces(m); |
|---|
| 114 | out << "[" << n << "] *" << REVISION(p) << " (" |
|---|
| 115 | << (dir_end - DIR_START)/D2 << ") " << block_usage(p) << "% "; |
|---|
| 116 | |
|---|
| 117 | for (c = DIR_START; c < dir_end; c += D2) { |
|---|
| 118 | if (c == DIR_START + 6) out << "... "; |
|---|
| 119 | if (c >= DIR_START + 6 && c < dir_end - 6) continue; |
|---|
| 120 | |
|---|
| 121 | print_key(p, c, j); |
|---|
| 122 | out << ' '; |
|---|
| 123 | } |
|---|
| 124 | out << endl; |
|---|
| 125 | } |
|---|
| 126 | |
|---|
| 127 | void BtreeCheck::failure(int n) const |
|---|
| 128 | { |
|---|
| 129 | out << "B-tree error " << n << endl; |
|---|
| 130 | throw "btree error"; |
|---|
| 131 | } |
|---|
| 132 | |
|---|
| 133 | void |
|---|
| 134 | BtreeCheck::block_check(Cursor_ * C_, int j, int opts) |
|---|
| 135 | { |
|---|
| 136 | byte * p = C_[j].p; |
|---|
| 137 | uint4 n = C_[j].n; |
|---|
| 138 | size_t c; |
|---|
| 139 | size_t significant_c = j == 0 ? DIR_START : DIR_START + D2; |
|---|
| 140 | |
|---|
| 141 | |
|---|
| 142 | size_t max_free = MAX_FREE(p); |
|---|
| 143 | size_t dir_end = DIR_END(p); |
|---|
| 144 | int total_free = block_size - dir_end; |
|---|
| 145 | |
|---|
| 146 | if (base.block_free_at_start(n)) failure(0); |
|---|
| 147 | if (base.block_free_now(n)) failure(1); |
|---|
| 148 | base.free_block(n); |
|---|
| 149 | |
|---|
| 150 | if (j != GET_LEVEL(p)) failure(10); |
|---|
| 151 | if (dir_end <= DIR_START || dir_end > block_size) failure(20); |
|---|
| 152 | |
|---|
| 153 | if (opts & OPT_SHORT_TREE) report_block(3*(level - j), n, p); |
|---|
| 154 | |
|---|
| 155 | if (opts & OPT_FULL_TREE) report_block_full(3*(level - j), n, p); |
|---|
| 156 | |
|---|
| 157 | for (c = DIR_START; c < dir_end; c += D2) { |
|---|
| 158 | Item_ item(p, c); |
|---|
| 159 | int o = item.get_address() - p; |
|---|
| 160 | if (o > int(block_size)) failure(21); |
|---|
| 161 | if (o - dir_end < max_free) failure(30); |
|---|
| 162 | |
|---|
| 163 | int kt_len = item.size(); |
|---|
| 164 | if (o + kt_len > int(block_size)) failure(40); |
|---|
| 165 | total_free -= kt_len; |
|---|
| 166 | |
|---|
| 167 | if (c > significant_c && Item_(p, c - D2).key() >= item.key()) |
|---|
| 168 | failure(50); |
|---|
| 169 | } |
|---|
| 170 | if (total_free != TOTAL_FREE(p)) |
|---|
| 171 | failure(60); |
|---|
| 172 | |
|---|
| 173 | if (j == 0) return; |
|---|
| 174 | for (c = DIR_START; c < dir_end; c += D2) { |
|---|
| 175 | C_[j].c = c; |
|---|
| 176 | block_to_cursor(C_, j - 1, Item_(p, c).block_given_by()); |
|---|
| 177 | |
|---|
| 178 | block_check(C_, j - 1, opts); |
|---|
| 179 | |
|---|
| 180 | byte * q = C_[j - 1].p; |
|---|
| 181 | |
|---|
| 182 | |
|---|
| 183 | |
|---|
| 184 | if (j == 1 && c > DIR_START) |
|---|
| 185 | if (Item_(q, DIR_START).key() < Item_(p, c).key()) |
|---|
| 186 | failure(70); |
|---|
| 187 | |
|---|
| 188 | |
|---|
| 189 | |
|---|
| 190 | |
|---|
| 191 | if (j > 1 && c > DIR_START && DIR_END(q) > DIR_START + D2 && |
|---|
| 192 | Item_(q, DIR_START + D2).key() < Item_(p, c).key()) |
|---|
| 193 | failure(80); |
|---|
| 194 | |
|---|
| 195 | |
|---|
| 196 | |
|---|
| 197 | |
|---|
| 198 | if (c + D2 < dir_end && |
|---|
| 199 | (j == 1 || DIR_START + D2 < DIR_END(q)) && |
|---|
| 200 | Item_(q, DIR_END(q) - D2).key() >= Item_(p, c + D2).key()) |
|---|
| 201 | failure(90); |
|---|
| 202 | |
|---|
| 203 | if (REVISION(q) > REVISION(p)) failure(91); |
|---|
| 204 | } |
|---|
| 205 | } |
|---|
| 206 | |
|---|
| 207 | void |
|---|
| 208 | BtreeCheck::check(const string & name, int opts, ostream &out) |
|---|
| 209 | { |
|---|
| 210 | BtreeCheck B(name, false, out); |
|---|
| 211 | B.open(); |
|---|
| 212 | Cursor_ * C = B.C; |
|---|
| 213 | |
|---|
| 214 | 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 | << " root="; |
|---|
| 222 | if (B.faked_root_block) |
|---|
| 223 | out << "(faked)"; |
|---|
| 224 | else |
|---|
| 225 | out << C[B.level].n; |
|---|
| 226 | out << endl; |
|---|
| 227 | } |
|---|
| 228 | |
|---|
| 229 | int limit = B.base.get_bit_map_size() - 1; |
|---|
| 230 | |
|---|
| 231 | limit = limit * CHAR_BIT + CHAR_BIT - 1; |
|---|
| 232 | |
|---|
| 233 | if (opts & OPT_SHOW_BITMAP) { |
|---|
| 234 | for (int j = 0; j <= limit; j++) { |
|---|
| 235 | out << (B.base.block_free_at_start(j) ? '.' : '*'); |
|---|
| 236 | if (j > 0) { |
|---|
| 237 | if ((j + 1) % 100 == 0) { |
|---|
| 238 | out << '\n'; |
|---|
| 239 | } else if ((j + 1) % 10 == 0) { |
|---|
| 240 | out << ' '; |
|---|
| 241 | } |
|---|
| 242 | } |
|---|
| 243 | } |
|---|
| 244 | out << '\n' << endl; |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | if (B.faked_root_block) { |
|---|
| 248 | if (opts) out << "void "; |
|---|
| 249 | } else { |
|---|
| 250 | B.block_check(C, B.level, opts); |
|---|
| 251 | |
|---|
| 252 | |
|---|
| 253 | |
|---|
| 254 | if (!B.base.is_empty()) { |
|---|
| 255 | B.failure(100); |
|---|
| 256 | } |
|---|
| 257 | } |
|---|
| 258 | if (opts) out << "B-tree checked okay" << endl; |
|---|
| 259 | } |
|---|
| 260 | |
|---|
| 261 | void BtreeCheck::report_cursor(int N, const Cursor_ * C_) const |
|---|
| 262 | { |
|---|
| 263 | out << N << ")\n"; |
|---|
| 264 | for (int i = 0; i <= level; i++) |
|---|
| 265 | out << "p=" << C_[i].p << ", c=" << C_[i].c << ", n=[" << C_[i].n |
|---|
| 266 | << "], rewrite=" << C_[i].rewrite << endl; |
|---|
| 267 | } |
|---|