| 243 | } |
| 244 | |
| 245 | bool BreakpointManager::ForcedBreakpointConditionsSatisfied(std::vector<CallStackFrame> const & stack, |
| 246 | Node * bpNode, BreakpointType bpType) |
| 247 | { |
| 248 | // Check if the current frame type is one we can break on |
| 249 | if (!(forceBreakpointMask_ & bpType)) { |
| 250 | return false; |
| 251 | } |
| 252 | |
| 253 | // Check if we're on the correct stack depth |
| 254 | if (stack.size() > maxBreakDepth_) { |
| 255 | return false; |
| 256 | } |
| 257 | |
| 258 | // Skip rule pushdown frames (avoids unnecessary additional single-stepping frame) |
| 259 | if (forceBreakpointFlags_ & ContinueSkipRulePushdown) { |
| 260 | if (bpType == BreakpointType::BreakOnPushDown |
| 261 | && bpNode != nullptr |
| 262 | && gNodeVMTWrappers->GetType(bpNode) == NodeType::Rule) { |
| 263 | return false; |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | // Skip database propagation nodes |
| 268 | if (forceBreakpointFlags_ & ContinueSkipDbPropagation) { |
| 269 | // Look for a likely database propagation signature in the call stack |
| 270 | // (an Insert/Delete frame followed by a Pushdown frame) |
| 271 | for (auto i = 0; i < stack.size() - 1; i++) { |
| 272 | auto & first = stack[i]; |
| 273 | auto & second = stack[i + 1]; |
| 274 | |
| 275 | if ((first.frameType == BreakpointReason::NodeInsertTuple |
| 276 | || first.frameType == BreakpointReason::NodeDeleteTuple) |
| 277 | && (second.frameType == BreakpointReason::NodePushDownTuple |
| 278 | || second.frameType == BreakpointReason::NodePushDownTupleDelete)) { |
| 279 | // Check whether the first node is a parent of the second node |
| 280 | auto secondType = gNodeVMTWrappers->GetType(second.node); |
| 281 | uint32_t parentNodeId; |
| 282 | if (secondType == NodeType::Rule || secondType == NodeType::RelOp) |
| 283 | { |
| 284 | auto rel = static_cast<RelNode *>(second.node); |
| 285 | parentNodeId = rel->Parent.Id; |
| 286 | } |
| 287 | else if (secondType == NodeType::And || secondType == NodeType::NotAnd) { |
| 288 | auto join = static_cast<JoinNode *>(second.node); |
| 289 | parentNodeId = join->Left.Id; |
| 290 | } |
| 291 | else { |
| 292 | Debug("Debugger::ForcedBreakpointConditionsSatisfied(): Illegal call order: %d --> %d", |
| 293 | first.node->Id, second.node->Id); |
| 294 | parentNodeId = first.node->Id; |
| 295 | } |
| 296 | |
| 297 | if (parentNodeId != first.node->Id) { |
| 298 | return false; |
| 299 | } |
| 300 | } |
| 301 | } |
| 302 | } |