/** * Recursively inspect btree buckets. * @param dl DiskLoc for the current bucket * @param depth depth for the current bucket (root is 0) * @param childNum so that the current bucket is the childNum-th child of its parent * (the right child is numbered as the last left child + 1) * @param parentIsExpanded bucket expansion was requested for the parent bucket so the * statistics and information for this bucket will appear in the * subtree * @param expandedAncestors if the d-th element is k, the k-th child of an expanded parent * at depth d is expanded * [0, 4, 1] means that root is expanded, its 4th child is expanded * and, in turn, the first child of the 4th child of the root is * expanded * @return true on success, false otherwise */ bool inspectBucket(const DiskLoc& dl, unsigned int depth, int childNum, bool parentIsExpanded, vector<int> expandedAncestors) { if (dl.isNull()) return true; killCurrentOp.checkForInterrupt(); const BtreeBucket<Version>* bucket = dl.btree<Version>(); int usedKeyCount = 0; // number of used keys in this bucket int keyCount = bucket->getN(); int childrenCount = keyCount + 1; // maximum number of children of this bucket // including the right child if (depth > _stats.depth) _stats.depth = depth; bool curNodeIsExpanded = false; if (parentIsExpanded) { // if the parent node is expanded, statistics and info will be outputted for this // bucket as well expandedAncestors.push_back(childNum); // if the expansion of this node was requested if (depth < _expandNodes.size() && _expandNodes[depth] == childNum) { verify(_stats.branch.size() == depth + 1); _stats.newBranchLevel(childrenCount); curNodeIsExpanded = true; } } const _KeyNode* firstKeyNode = NULL; const _KeyNode* lastKeyNode = NULL; for (int i = 0; i < keyCount; i++ ) { const _KeyNode& kn = bucket->k(i); if (kn.isUsed()) { ++usedKeyCount; if (i == 0) { firstKeyNode = &kn; } lastKeyNode = &kn; this->inspectBucket(kn.prevChildBucket, depth + 1, i, curNodeIsExpanded, expandedAncestors); } } this->inspectBucket(bucket->getNextChild(), depth + 1, keyCount, curNodeIsExpanded, expandedAncestors); killCurrentOp.checkForInterrupt(); if (parentIsExpanded) { // stats for the children of this bucket have been added in the recursive calls, // avoid including the current bucket in the stats for its subtree expandedAncestors.pop_back(); } // add the stats for the current bucket to the aggregates for all its ancestors and // the entire tree for (unsigned int d = 0; d < expandedAncestors.size(); ++d) { AreaStats& nodeStats = _stats.nodeAt(d, expandedAncestors[d]); nodeStats.addStats(keyCount, usedKeyCount, bucket, sizeof(_KeyNode)); } _stats.wholeTree.addStats(keyCount, usedKeyCount, bucket, sizeof(_KeyNode)); if (parentIsExpanded) { NodeInfo nodeInfo; if (firstKeyNode != NULL) { nodeInfo.firstKey = KeyNode(*bucket, *firstKeyNode).key.toBson(); } if (lastKeyNode != NULL) { nodeInfo.lastKey = KeyNode(*bucket, *lastKeyNode).key.toBson(); } nodeInfo.childNum = childNum; nodeInfo.depth = depth; nodeInfo.diskLoc = dl.toBSONObj(); nodeInfo.keyCount = keyCount; nodeInfo.usedKeyCount = bucket->getN(); nodeInfo.fillRatio = (1.0 - static_cast<double>(bucket->getEmptySize()) / BucketBasics::bodySize()); _stats.nodeAt(depth, childNum).nodeInfo = nodeInfo; } // add the stats for this bucket to the aggregate for a certain depth while (_stats.perLevel.size() < depth + 1) _stats.perLevel.push_back(AreaStats()); verify(_stats.perLevel.size() > depth); AreaStats& level = _stats.perLevel[depth]; level.addStats(keyCount, usedKeyCount, bucket, sizeof(_KeyNode)); return true; }