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