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 = 107 Solution.hasValue() ? "\n" + debugString(Solution.getValue()) : ""; 108 109 return formatv( 110 Template, 111 llvm::make_range(ConstraintsStrings.begin(), ConstraintsStrings.end()), 112 StatusString, SolutionString); 113 } 114 115 private: 116 /// Returns a string representation of a truth assignment to atom booleans. 117 std::string debugString( 118 const llvm::DenseMap<AtomicBoolValue *, Solver::Result::Assignment> 119 &AtomAssignments) { 120 size_t MaxNameLength = 0; 121 for (auto &AtomName : AtomNames) { 122 MaxNameLength = std::max(MaxNameLength, AtomName.second.size()); 123 } 124 125 std::vector<std::string> Lines; 126 for (auto &AtomAssignment : AtomAssignments) { 127 auto Line = formatv("{0} = {1}", 128 fmt_align(getAtomName(AtomAssignment.first), 129 AlignStyle::Left, MaxNameLength), 130 debugString(AtomAssignment.second)); 131 Lines.push_back(Line); 132 } 133 llvm::sort(Lines.begin(), Lines.end()); 134 135 return formatv("{0:$[\n]}", llvm::make_range(Lines.begin(), Lines.end())); 136 } 137 138 /// Returns a string representation of a boolean assignment to true or false. 139 std::string debugString(Solver::Result::Assignment Assignment) { 140 switch (Assignment) { 141 case Solver::Result::Assignment::AssignedFalse: 142 return "False"; 143 case Solver::Result::Assignment::AssignedTrue: 144 return "True"; 145 } 146 llvm_unreachable("Booleans can only be assigned true/false"); 147 } 148 149 /// Returns a string representation of the result status of a SAT check. 150 std::string debugString(Solver::Result::Status Status) { 151 switch (Status) { 152 case Solver::Result::Status::Satisfiable: 153 return "Satisfiable"; 154 case Solver::Result::Status::Unsatisfiable: 155 return "Unsatisfiable"; 156 case Solver::Result::Status::TimedOut: 157 return "TimedOut"; 158 } 159 llvm_unreachable("Unhandled SAT check result status"); 160 } 161 162 /// Returns the name assigned to `Atom`, either user-specified or created by 163 /// default rules (B0, B1, ...). 164 std::string getAtomName(const AtomicBoolValue *Atom) { 165 auto Entry = AtomNames.try_emplace(Atom, formatv("B{0}", Counter)); 166 if (Entry.second) { 167 Counter++; 168 } 169 return Entry.first->second; 170 } 171 172 // Keep track of number of atoms without a user-specified name, used to assign 173 // non-repeating default names to such atoms. 174 size_t Counter; 175 176 // Keep track of names assigned to atoms. 177 llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames; 178 }; 179 180 } // namespace 181 182 std::string 183 debugString(const BoolValue &B, 184 llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames) { 185 return DebugStringGenerator(std::move(AtomNames)).debugString(B); 186 } 187 188 std::string 189 debugString(const std::vector<BoolValue *> &Constraints, 190 const Solver::Result &Result, 191 llvm::DenseMap<const AtomicBoolValue *, std::string> AtomNames) { 192 return DebugStringGenerator(std::move(AtomNames)) 193 .debugString(Constraints, Result); 194 } 195 196 } // namespace dataflow 197 } // namespace clang 198