[btree_node_checker] first draft
Spin-off from btree_damage_visitor
This commit is contained in:
		| @@ -67,6 +67,7 @@ SOURCE=\ | ||||
| 	persistent-data/data-structures/bitset.cc \ | ||||
| 	persistent-data/data-structures/bloom_filter.cc \ | ||||
| 	persistent-data/data-structures/btree.cc \ | ||||
| 	persistent-data/data-structures/btree_node_checker.cc \ | ||||
| 	persistent-data/error_set.cc \ | ||||
| 	persistent-data/file_utils.cc \ | ||||
| 	persistent-data/hex_dump.cc \ | ||||
|   | ||||
							
								
								
									
										127
									
								
								persistent-data/data-structures/btree_node_checker.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								persistent-data/data-structures/btree_node_checker.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| #include "btree_node_checker.h" | ||||
|  | ||||
| #include <sstream> | ||||
|  | ||||
| using persistent_data::btree_detail::btree_node_checker; | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| btree_node_checker::error_type btree_node_checker::get_last_error() { | ||||
| 	return last_error_; | ||||
| } | ||||
|  | ||||
| std::string btree_node_checker::get_last_error_string() { | ||||
| 	switch (last_error_) { | ||||
| 	case BLOCK_NR_MISMATCH: | ||||
| 		return block_nr_mismatch_string(); | ||||
| 	case VALUE_SIZES_MISMATCH: | ||||
| 		return value_sizes_mismatch_string(); | ||||
| 	case MAX_ENTRIES_TOO_LARGE: | ||||
| 		return max_entries_too_large_string(); | ||||
| 	case MAX_ENTRIES_NOT_DIVISIBLE: | ||||
| 		return max_entries_not_divisible_string(); | ||||
| 	case NR_ENTRIES_TOO_LARGE: | ||||
| 		return nr_entries_too_large_string(); | ||||
| 	case NR_ENTRIES_TOO_SMALL: | ||||
| 		return nr_entries_too_small_string(); | ||||
| 	case KEYS_OUT_OF_ORDER: | ||||
| 		return keys_out_of_order_string(); | ||||
| 	case PARENT_KEY_MISMATCH: | ||||
| 		return parent_key_mismatch_string(); | ||||
| 	case LEAF_KEY_OVERLAPPED: | ||||
| 		return leaf_key_overlapped_string(); | ||||
| 	default: | ||||
| 		return std::string(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void btree_node_checker::reset() { | ||||
| 	last_error_ = NO_ERROR; | ||||
| } | ||||
|  | ||||
| std::string btree_node_checker::block_nr_mismatch_string() { | ||||
| 	std::ostringstream out; | ||||
| 	out << "block number mismatch: actually " | ||||
| 	    << error_location_ | ||||
| 	    << ", claims " << error_block_nr_; | ||||
|  | ||||
| 	return out.str(); | ||||
| } | ||||
|  | ||||
| std::string btree_node_checker::value_sizes_mismatch_string() { | ||||
| 	std::ostringstream out; | ||||
| 	out << "value size mismatch: expected " << error_value_sizes_[1] | ||||
| 	    << ", but got " << error_value_sizes_[0] | ||||
| 	    << ". This is not the btree you are looking for." | ||||
| 	    << " (block " << error_location_ << ")"; | ||||
|  | ||||
| 	return out.str(); | ||||
| } | ||||
|  | ||||
| std::string btree_node_checker::max_entries_too_large_string() { | ||||
| 	std::ostringstream out; | ||||
| 	out << "max entries too large: " << error_max_entries_ | ||||
| 	    << " (block " << error_location_ << ")"; | ||||
|  | ||||
| 	return out.str(); | ||||
| } | ||||
|  | ||||
| std::string btree_node_checker::max_entries_not_divisible_string() { | ||||
| 	std::ostringstream out; | ||||
| 	out << "max entries is not divisible by 3: " << error_max_entries_ | ||||
| 	    << " (block " << error_location_ << ")"; | ||||
|  | ||||
| 	return out.str(); | ||||
| } | ||||
|  | ||||
| std::string btree_node_checker::nr_entries_too_large_string() { | ||||
| 	std::ostringstream out; | ||||
| 	out << "bad nr_entries: " | ||||
| 	    << error_nr_entries_ << " < " | ||||
| 	    << error_max_entries_ | ||||
| 	    << " (block " << error_location_ << ")"; | ||||
|  | ||||
| 	return out.str(); | ||||
| } | ||||
|  | ||||
| std::string btree_node_checker::nr_entries_too_small_string() { | ||||
| 	std::ostringstream out; | ||||
| 	out << "too few entries in btree_node: " | ||||
| 	    << error_nr_entries_ | ||||
| 	    << ", expected at least " | ||||
| 	    << (error_max_entries_ / 3) | ||||
| 	    << " (block " << error_location_ | ||||
| 	    << ", max_entries = " << error_max_entries_ << ")"; | ||||
|  | ||||
| 	return out.str(); | ||||
| } | ||||
|  | ||||
| std::string btree_node_checker::keys_out_of_order_string() { | ||||
| 	std::ostringstream out; | ||||
| 	out << "keys are out of order, " | ||||
| 	    << error_keys_[0] << " <= " << error_keys_[1] | ||||
| 	    << " (block " << error_location_ << ")"; | ||||
|  | ||||
| 	return out.str(); | ||||
| } | ||||
|  | ||||
| std::string btree_node_checker::parent_key_mismatch_string() { | ||||
| 	std::ostringstream out; | ||||
| 	out << "parent key mismatch: parent was " << error_keys_[1] | ||||
| 	    << ", but lowest in node was " << error_keys_[0] | ||||
| 	    << " (block " << error_location_ << ")"; | ||||
|  | ||||
| 	return out.str(); | ||||
| } | ||||
|  | ||||
| std::string btree_node_checker::leaf_key_overlapped_string() { | ||||
| 	std::ostringstream out; | ||||
| 	out << "the last key of the previous leaf was " << error_keys_[1] | ||||
| 	    << " and the first key of this leaf is " << error_keys_[0] | ||||
| 	    << " (block " << error_location_ << ")"; | ||||
|  | ||||
| 	return out.str(); | ||||
| } | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
							
								
								
									
										206
									
								
								persistent-data/data-structures/btree_node_checker.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								persistent-data/data-structures/btree_node_checker.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,206 @@ | ||||
| #ifndef BTREE_NODE_CHECKER_H | ||||
| #define BTREE_NODE_CHECKER_H | ||||
|  | ||||
| #include "block-cache/block_cache.h" | ||||
| #include "persistent-data/block.h" | ||||
| #include "persistent-data/data-structures/btree.h" | ||||
| #include "persistent-data/data-structures/btree_disk_structures.h" | ||||
|  | ||||
| #include <boost/optional.hpp> | ||||
| #include <string> | ||||
|  | ||||
| using bcache::block_address; | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| namespace persistent_data { | ||||
| 	namespace btree_detail { | ||||
| 		class btree_node_checker { | ||||
| 		public: | ||||
| 			enum error_type { | ||||
| 				NO_ERROR, | ||||
| 				BLOCK_NR_MISMATCH, | ||||
| 				VALUE_SIZES_MISMATCH, | ||||
| 				MAX_ENTRIES_TOO_LARGE, | ||||
| 				MAX_ENTRIES_NOT_DIVISIBLE, | ||||
| 				NR_ENTRIES_TOO_LARGE, | ||||
| 				NR_ENTRIES_TOO_SMALL, | ||||
| 				KEYS_OUT_OF_ORDER, | ||||
| 				VALUE_SIZE_MISMATCH, | ||||
| 				PARENT_KEY_MISMATCH, | ||||
| 				LEAF_KEY_OVERLAPPED, | ||||
| 			}; | ||||
|  | ||||
| 			btree_node_checker(): | ||||
| 				last_error_(NO_ERROR), | ||||
| 				error_location_(0), | ||||
| 				error_block_nr_(0), | ||||
| 				error_nr_entries_(0), | ||||
| 				error_max_entries_(0), | ||||
| 				error_value_sizes_{0, 0}, | ||||
| 				error_keys_{0, 0} { | ||||
| 			} | ||||
|  | ||||
| 			template <typename ValueTraits> | ||||
| 			bool check_block_nr(btree_detail::node_ref<ValueTraits> const &n) { | ||||
| 				if (n.get_location() != n.get_block_nr()) { | ||||
| 					last_error_ = BLOCK_NR_MISMATCH; | ||||
| 					error_block_nr_ = n.get_block_nr(); | ||||
| 					error_location_ = n.get_location(); | ||||
|  | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				return true; | ||||
| 			} | ||||
|  | ||||
| 			template <typename ValueTraits> | ||||
| 			bool check_value_size(btree_detail::node_ref<ValueTraits> const &n) { | ||||
| 				if (!n.value_sizes_match()) { | ||||
| 					last_error_ = VALUE_SIZES_MISMATCH; | ||||
| 					error_location_ = n.get_location(); | ||||
| 					error_value_sizes_[0] = n.get_value_size(); | ||||
| 					error_value_sizes_[1] = sizeof(typename ValueTraits::disk_type); | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				return true; | ||||
| 			} | ||||
|  | ||||
| 			template <typename ValueTraits> | ||||
| 			bool check_max_entries(btree_detail::node_ref<ValueTraits> const &n) { | ||||
| 				size_t elt_size = sizeof(uint64_t) + n.get_value_size(); | ||||
| 				if (elt_size * n.get_max_entries() + sizeof(node_header) > MD_BLOCK_SIZE) { | ||||
| 					last_error_ = MAX_ENTRIES_TOO_LARGE; | ||||
| 					error_location_ = n.get_location(); | ||||
| 					error_max_entries_ = n.get_max_entries(); | ||||
|  | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				if (n.get_max_entries() % 3) { | ||||
| 					last_error_ = MAX_ENTRIES_NOT_DIVISIBLE; | ||||
| 					error_location_ = n.get_location(); | ||||
| 					error_max_entries_ = n.get_max_entries(); | ||||
|  | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				return true; | ||||
| 			} | ||||
|  | ||||
| 			template <typename ValueTraits> | ||||
| 			bool check_nr_entries(btree_detail::node_ref<ValueTraits> const &n, | ||||
| 					      bool is_root) { | ||||
| 				if (n.get_nr_entries() > n.get_max_entries()) { | ||||
| 					last_error_ = NR_ENTRIES_TOO_LARGE; | ||||
| 					error_location_ = n.get_location(); | ||||
| 					error_nr_entries_ = n.get_nr_entries(); | ||||
| 					error_max_entries_ = n.get_max_entries(); | ||||
|  | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				block_address min = n.get_max_entries() / 3; | ||||
| 				if (!is_root && (n.get_nr_entries() < min)) { | ||||
| 					last_error_ = NR_ENTRIES_TOO_SMALL; | ||||
| 					error_location_ = n.get_location(); | ||||
| 					error_nr_entries_ = n.get_nr_entries(); | ||||
| 					error_max_entries_ = n.get_max_entries(); | ||||
|  | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				return true; | ||||
| 			} | ||||
|  | ||||
| 			template <typename ValueTraits> | ||||
| 			bool check_ordered_keys(btree_detail::node_ref<ValueTraits> const &n) { | ||||
| 				unsigned nr_entries = n.get_nr_entries(); | ||||
|  | ||||
| 				if (nr_entries == 0) | ||||
| 					return true; // can only happen if a root node | ||||
|  | ||||
| 				uint64_t last_key = n.key_at(0); | ||||
|  | ||||
| 				for (unsigned i = 1; i < nr_entries; i++) { | ||||
| 					uint64_t k = n.key_at(i); | ||||
| 					if (k <= last_key) { | ||||
| 						last_error_ = KEYS_OUT_OF_ORDER; | ||||
| 						error_location_ = n.get_location(); | ||||
| 						error_keys_[0] = k; | ||||
| 						error_keys_[1] = last_key; | ||||
|  | ||||
| 						return false; | ||||
| 					} | ||||
| 					last_key = k; | ||||
| 				} | ||||
|  | ||||
| 				return true; | ||||
| 			} | ||||
|  | ||||
| 			template <typename ValueTraits> | ||||
| 			bool check_parent_key(btree_detail::node_ref<ValueTraits> const &n, | ||||
| 					      boost::optional<uint64_t> key) { | ||||
| 				if (!key) | ||||
| 					return true; | ||||
|  | ||||
| 				if (*key > n.key_at(0)) { | ||||
| 					last_error_ = PARENT_KEY_MISMATCH; | ||||
| 					error_location_ = n.get_location(); | ||||
| 					error_keys_[0] = n.key_at(0); | ||||
| 					error_keys_[1] = *key; | ||||
|  | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				return true; | ||||
| 			} | ||||
|  | ||||
| 			template <typename ValueTraits> | ||||
| 			bool check_leaf_key(btree_detail::node_ref<ValueTraits> const &n, | ||||
| 					    boost::optional<uint64_t> key) { | ||||
| 				if (n.get_nr_entries() == 0) | ||||
| 					return true; // can only happen if a root node | ||||
|  | ||||
| 				if (key && *key >= n.key_at(0)) { | ||||
| 					last_error_ = LEAF_KEY_OVERLAPPED; | ||||
| 					error_location_ = n.get_location(); | ||||
| 					error_keys_[0] = n.key_at(0); | ||||
| 					error_keys_[1] = *key; | ||||
|  | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				return true; | ||||
| 			} | ||||
|  | ||||
| 			error_type get_last_error(); | ||||
| 			std::string get_last_error_string(); | ||||
| 			void reset(); | ||||
|  | ||||
| 		private: | ||||
| 			std::string block_nr_mismatch_string(); | ||||
| 			std::string value_sizes_mismatch_string(); | ||||
| 			std::string max_entries_too_large_string(); | ||||
| 			std::string max_entries_not_divisible_string(); | ||||
| 			std::string nr_entries_too_large_string(); | ||||
| 			std::string nr_entries_too_small_string(); | ||||
| 			std::string keys_out_of_order_string(); | ||||
| 			std::string parent_key_mismatch_string(); | ||||
| 			std::string leaf_key_overlapped_string(); | ||||
|  | ||||
| 			error_type last_error_; | ||||
| 			block_address error_location_; | ||||
| 			block_address error_block_nr_; | ||||
| 			uint32_t error_nr_entries_; | ||||
| 			uint32_t error_max_entries_; | ||||
| 			uint32_t error_value_sizes_[2]; | ||||
| 			uint64_t error_keys_[2]; | ||||
| 		}; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| //---------------------------------------------------------------- | ||||
|  | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user