1 //===- DebugSupport.cpp -----------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines functions which generate more readable forms of data 10 // structures used in the dataflow analyses, for debugging purposes. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include <utility> 15 16 #include "clang/Analysis/FlowSensitive/DebugSupport.h" 17 #include "clang/Analysis/FlowSensitive/Solver.h" 18 #include "clang/Analysis/FlowSensitive/Value.h" 19 #include "llvm/ADT/DenseMap.h" 20 #include "llvm/ADT/StringSet.h" 21 #include "llvm/Support/ErrorHandling.h" 22 #include "llvm/Support/FormatAdapters.h" 23 #include "llvm/Support/FormatCommon.h" 24 #include "llvm/Support/FormatVariadic.h" 25 26 namespace clang { 27 namespace dataflow { 28 29 using llvm::AlignStyle; 30 using llvm::fmt_pad; 31 using llvm::formatv; 32 33 std::string debugString(Solver::Result::Assignment Assignment) { 34 switch (Assignment) { 35 case Solver::Result::Assignment::AssignedFalse: 36 return "False"; 37 case Solver::Result::Assignment::AssignedTrue: 38 return "True"; 39 } 40 llvm_unreachable("Booleans can only be assigned true/false"); 41 } 42 43 std::string debugString(Solver::Result::Status Status) { 44 switch (Status) { 45 case Solver::Result::Status::Satisfiable: 46 return "Satisfiable"; 47 case Solver::Result::Status::Unsatisfiable: 48 return "Unsatisfiable"; 49 case Solver::Result::Status::TimedOut: 50 return "TimedOut"; 51 } 52 llvm_unreachable("Unhandled SAT check result status"); 53 } 54 55 namespace { 56 57 class DebugStringGenerator { 58 public: 59 explicit DebugStringGenerator( 60 llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNamesArg) 61 : Counter(0), AtomNames(std::move(AtomNamesArg)) { 62 #ifndef NDEBUG 63 llvm::StringSet<> Names; 64 for (auto &N : AtomNames) { 65 assert(Names.insert(N.second).second && 66 "The same name must not assigned to different atoms"); 67 } 68 #endif 69 } 70 71 /// Returns a string representation of a boolean value `B`. 72 std::string debugString(const BoolValue &B, size_t Depth = 0) { 73 std::string S; 74 switch (B.getKind()) { 75 case Value::Kind::AtomicBool: { 76 S = getAtomName(&cast<AtomicBoolValue>(B)); 77 break; 78 } 79 case Value::Kind::Conjunction: { 80 auto &C = cast<ConjunctionValue>(B); 81 auto L = debugString(C.getLeftSubValue(), Depth + 1); 82 auto R = debugString(C.getRightSubValue(), Depth + 1); 83 S = formatv("(and\n{0}\n{1})", L, R); 84 break; 85 } 86 case Value::Kind::Disjunction: { 87 auto &D = cast<DisjunctionValue>(B); 88 auto L = debugString(D.getLeftSubValue(), Depth + 1); 89 auto R = debugString(D.getRightSubValue(), Depth + 1); 90 S = formatv("(or\n{0}\n{1})", L, R); 91 break; 92 } 93 case Value::Kind::Negation: { 94 auto &N = cast<NegationValue>(B); 95 S = formatv("(not\n{0})", debugString(N.getSubVal(), Depth + 1)); 96 break; 97 } 98 default: 99 llvm_unreachable("Unhandled value kind"); 100 } 101 auto Indent = Depth * 4; 102 return formatv("{0}", fmt_pad(S, Indent, 0)); 103 } 104 105 /// Returns a string representation of a set of boolean `Constraints` and the 106 /// `Result` of satisfiability checking on the `Constraints`. 107 std::string debugString(ArrayRef<BoolValue *> &Constraints, 108 const Solver::Result &Result) { 109 auto Template = R"( 110 Constraints 111 ------------ 112 {0:$[ 113 114 ]} 115 ------------ 116 {1}. 117 {2} 118 )"; 119 120 std::vector<std::string> ConstraintsStrings; 121 ConstraintsStrings.reserve(Constraints.size()); 122 for (auto &Constraint : Constraints) { 123 ConstraintsStrings.push_back(debugString(*Constraint)); 124 } 125 126 auto StatusString = clang::dataflow::debugString(Result.getStatus()); 127 auto Solution = Result.getSolution(); 128 auto SolutionString = Solution ? "\n" + debugString(Solution.value()) : ""; 129 130 return formatv( 131 Template, 132 llvm::make_range(ConstraintsStrings.begin(), ConstraintsStrings.end()), 133 StatusString, SolutionString); 134 } 135 136 private: 137 /// Returns a string representation of a truth assignment to atom booleans. 138 std::string debugString( 139 const llvm::DenseMap<AtomicBoolValue *, Solver::Result::Assignment> 140 &AtomAssignments) { 141 size_t MaxNameLength = 0; 142 for (auto &AtomName : AtomNames) { 143 MaxNameLength = std::max(MaxNameLength, AtomName.second.size()); 144 } 145 146 std::vector<std::string> Lines; 147 for (auto &AtomAssignment : AtomAssignments) { 148 auto Line = formatv("{0} = {1}", 149 fmt_align(getAtomName(AtomAssignment.first), 150 AlignStyle::Left, MaxNameLength), 151 clang::dataflow::debugString(AtomAssignment.second)); 152 Lines.push_back(Line); 153 } 154 llvm::sort(Lines.begin(), Lines.end()); 155 156 return formatv("{0:$[\n]}", llvm::make_range(Lines.begin(), Lines.end())); 157 } 158 159 /// Returns the name assigned to `Atom`, either user-specified or created by 160 /// default rules (B0, B1, ...). 161 std::string getAtomName(const AtomicBoolValue *Atom) { 162 auto Entry = AtomNames.try_emplace(Atom, formatv("B{0}", Counter)); 163 if (Entry.second) { 164 Counter++; 165 } 166 return Entry.first->second; 167 } 168 169 // Keep track of number of atoms without a user-specified name, used to assign 170 // non-repeating default names to such atoms. 171 size_t Counter; 172 173 // Keep track of names assigned to atoms. 174 llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames; 175 }; 176 177 } // namespace 178 179 std::string 180 debugString(const BoolValue &B, 181 llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames) { 182 return DebugStringGenerator(std::move(AtomNames)).debugString(B); 183 } 184 185 std::string 186 debugString(ArrayRef<BoolValue *> Constraints, const Solver::Result &Result, 187 llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames) { 188 return DebugStringGenerator(std::move(AtomNames)) 189 .debugString(Constraints, Result); 190 } 191 192 } // namespace dataflow 193 } // namespace clang 194