xref: /llvm-project/clang/lib/Analysis/FlowSensitive/DebugSupport.cpp (revision ee6aba85aa48d03a931ba989ea2c1584b468588a)
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