//===-- ExecutionTree.cpp -------------------------------------------------===// // // The KLEE Symbolic Virtual Machine // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ExecutionTree.h" #include "ExecutionState.h" #include "klee/Core/Interpreter.h" #include "klee/Expr/ExprPPrinter.h" #include "klee/Module/KInstruction.h" #include "klee/Support/OptionCategories.h" #include #include using namespace klee; namespace klee { llvm::cl::OptionCategory ExecTreeCat("Execution tree related options", "These options affect the execution tree handling."); } namespace { llvm::cl::opt CompressExecutionTree( "compress-execution-tree", llvm::cl::desc("Remove intermediate nodes in the execution " "tree whenever possible (default=false)"), llvm::cl::init(false), llvm::cl::cat(ExecTreeCat)); llvm::cl::opt WriteExecutionTree( "write-exec-tree", llvm::cl::init(false), llvm::cl::desc("Write execution tree into exec_tree.db (default=false)"), llvm::cl::cat(ExecTreeCat)); } // namespace // ExecutionTreeNode ExecutionTreeNode::ExecutionTreeNode(ExecutionTreeNode *parent, ExecutionState *state) noexcept : parent{parent}, left{nullptr}, right{nullptr}, state{state} { state->executionTreeNode = this; } // AnnotatedExecutionTreeNode AnnotatedExecutionTreeNode::AnnotatedExecutionTreeNode( ExecutionTreeNode *parent, ExecutionState *state) noexcept : ExecutionTreeNode(parent, state) { id = nextID++; } // NoopExecutionTree void NoopExecutionTree::dump(llvm::raw_ostream &os) noexcept { os << "digraph G {\nTreeNotAvailable [shape=box]\n}"; } // InMemoryExecutionTree InMemoryExecutionTree::InMemoryExecutionTree( ExecutionState &initialState) noexcept { root = ExecutionTreeNodePtr(createNode(nullptr, &initialState)); initialState.executionTreeNode = root.getPointer(); } ExecutionTreeNode *InMemoryExecutionTree::createNode(ExecutionTreeNode *parent, ExecutionState *state) { return new ExecutionTreeNode(parent, state); } void InMemoryExecutionTree::attach(ExecutionTreeNode *node, ExecutionState *leftState, ExecutionState *rightState, BranchType reason) noexcept { assert(node && !node->left.getPointer() && !node->right.getPointer()); assert(node == rightState->executionTreeNode && "Attach assumes the right state is the current state"); node->left = ExecutionTreeNodePtr(createNode(node, leftState)); // The current node inherits the tag uint8_t currentNodeTag = root.getInt(); if (node->parent) currentNodeTag = node->parent->left.getPointer() == node ? node->parent->left.getInt() : node->parent->right.getInt(); node->right = ExecutionTreeNodePtr(createNode(node, rightState), currentNodeTag); updateBranchingNode(*node, reason); node->state = nullptr; } void InMemoryExecutionTree::remove(ExecutionTreeNode *n) noexcept { assert(!n->left.getPointer() && !n->right.getPointer()); updateTerminatingNode(*n); do { ExecutionTreeNode *p = n->parent; if (p) { if (n == p->left.getPointer()) { p->left = ExecutionTreeNodePtr(nullptr); } else { assert(n == p->right.getPointer()); p->right = ExecutionTreeNodePtr(nullptr); } } delete n; n = p; } while (n && !n->left.getPointer() && !n->right.getPointer()); if (n && CompressExecutionTree) { // We are now at a node that has exactly one child; we've just deleted the // other one. Eliminate the node and connect its child to the parent // directly (if it's not the root). ExecutionTreeNodePtr child = n->left.getPointer() ? n->left : n->right; ExecutionTreeNode *parent = n->parent; child.getPointer()->parent = parent; if (!parent) { // We are at the root root = child; } else { if (n == parent->left.getPointer()) { parent->left = child; } else { assert(n == parent->right.getPointer()); parent->right = child; } } delete n; } } void InMemoryExecutionTree::dump(llvm::raw_ostream &os) noexcept { std::unique_ptr pp(ExprPPrinter::create(os)); pp->setNewline("\\l"); os << "digraph G {\n" << "\tsize=\"10,7.5\";\n" << "\tratio=fill;\n" << "\trotate=90;\n" << "\tcenter = \"true\";\n" << "\tnode [style=\"filled\",width=.1,height=.1,fontname=\"Terminus\"]\n" << "\tedge [arrowsize=.3]\n"; std::vector stack; stack.push_back(root.getPointer()); while (!stack.empty()) { const ExecutionTreeNode *n = stack.back(); stack.pop_back(); os << "\tn" << n << " [shape=diamond"; if (n->state) os << ",fillcolor=green"; os << "];\n"; if (n->left.getPointer()) { os << "\tn" << n << " -> n" << n->left.getPointer() << " [label=0b" << std::bitset(n->left.getInt()).to_string() << "];\n"; stack.push_back(n->left.getPointer()); } if (n->right.getPointer()) { os << "\tn" << n << " -> n" << n->right.getPointer() << " [label=0b" << std::bitset(n->right.getInt()).to_string() << "];\n"; stack.push_back(n->right.getPointer()); } } os << "}\n"; } std::uint8_t InMemoryExecutionTree::getNextId() noexcept { static_assert(PtrBitCount <= 8); std::uint8_t id = 1 << registeredIds++; if (registeredIds > PtrBitCount) { klee_error("ExecutionTree cannot support more than %d RandomPathSearchers", PtrBitCount); } return id; } // PersistentExecutionTree PersistentExecutionTree::PersistentExecutionTree( ExecutionState &initialState, InterpreterHandler &ih) noexcept : writer(ih.getOutputFilename("exec_tree.db")) { root = ExecutionTreeNodePtr(createNode(nullptr, &initialState)); initialState.executionTreeNode = root.getPointer(); } void PersistentExecutionTree::dump(llvm::raw_ostream &os) noexcept { writer.batchCommit(true); InMemoryExecutionTree::dump(os); } ExecutionTreeNode * PersistentExecutionTree::createNode(ExecutionTreeNode *parent, ExecutionState *state) { return new AnnotatedExecutionTreeNode(parent, state); } void PersistentExecutionTree::setTerminationType(ExecutionState &state, StateTerminationType type) { auto *annotatedNode = llvm::cast(state.executionTreeNode); annotatedNode->kind = type; } void PersistentExecutionTree::updateBranchingNode(ExecutionTreeNode &node, BranchType reason) { auto *annotatedNode = llvm::cast(&node); const auto &state = *node.state; const auto prevPC = state.prevPC; annotatedNode->asmLine = prevPC && prevPC->info ? prevPC->info->assemblyLine : 0; annotatedNode->kind = reason; writer.write(*annotatedNode); } void PersistentExecutionTree::updateTerminatingNode(ExecutionTreeNode &node) { assert(node.state); auto *annotatedNode = llvm::cast(&node); const auto &state = *node.state; const auto prevPC = state.prevPC; annotatedNode->asmLine = prevPC && prevPC->info ? prevPC->info->assemblyLine : 0; annotatedNode->stateID = state.getID(); writer.write(*annotatedNode); } // Factory std::unique_ptr klee::createExecutionTree(ExecutionState &initialState, bool inMemory, InterpreterHandler &ih) { if (WriteExecutionTree) return std::make_unique(initialState, ih); if (inMemory) return std::make_unique(initialState); return std::make_unique(); };