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 : }
|