Author: Gabor Marton Date: 2021-07-14T13:45:02+02:00 New Revision: bdf31471c76b5ded9e8d5a039250c2a7ba7aead6
URL: https://github.com/llvm/llvm-project/commit/bdf31471c76b5ded9e8d5a039250c2a7ba7aead6 DIFF: https://github.com/llvm/llvm-project/commit/bdf31471c76b5ded9e8d5a039250c2a7ba7aead6.diff LOG: [Analyzer][solver] Add dump methods for (dis)equality classes. This proved to be very useful during debugging. Differential Revision: https://reviews.llvm.org/D103967 Added: clang/test/Analysis/expr-inspection-printState-diseq-info.c clang/test/Analysis/expr-inspection-printState-eq-classes.c Modified: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp clang/test/Analysis/expr-inspection.c Removed: ################################################################################ diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 9e014e9fce4e2..aba97e48756aa 100644 --- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -592,6 +592,11 @@ class EquivalenceClass : public llvm::FoldingSetNode { RangeSet::Factory &F, ProgramStateRef State); + void dumpToStream(ProgramStateRef State, raw_ostream &os) const; + LLVM_DUMP_METHOD void dump(ProgramStateRef State) const { + dumpToStream(State, llvm::errs()); + } + /// Check equivalence data for consistency. LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool isClassDataConsistent(ProgramStateRef State); @@ -1599,6 +1604,15 @@ class RangeConstraintManager : public RangedConstraintManager { void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n", unsigned int Space = 0, bool IsDot = false) const override; + void printConstraints(raw_ostream &Out, ProgramStateRef State, + const char *NL = "\n", unsigned int Space = 0, + bool IsDot = false) const; + void printEquivalenceClasses(raw_ostream &Out, ProgramStateRef State, + const char *NL = "\n", unsigned int Space = 0, + bool IsDot = false) const; + void printDisequalities(raw_ostream &Out, ProgramStateRef State, + const char *NL = "\n", unsigned int Space = 0, + bool IsDot = false) const; //===------------------------------------------------------------------===// // Implementation for interface from RangedConstraintManager. @@ -1749,6 +1763,15 @@ ConstraintMap ento::getConstraintMap(ProgramStateRef State) { // EqualityClass implementation details //===----------------------------------------------------------------------===// +LLVM_DUMP_METHOD void EquivalenceClass::dumpToStream(ProgramStateRef State, + raw_ostream &os) const { + SymbolSet ClassMembers = getClassMembers(State); + for (const SymbolRef &MemberSym : ClassMembers) { + MemberSym->dump(); + os << "\n"; + } +} + inline EquivalenceClass EquivalenceClass::find(ProgramStateRef State, SymbolRef Sym) { assert(State && "State should not be null"); @@ -2601,6 +2624,16 @@ ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange( void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State, const char *NL, unsigned int Space, bool IsDot) const { + printConstraints(Out, State, NL, Space, IsDot); + printEquivalenceClasses(Out, State, NL, Space, IsDot); + printDisequalities(Out, State, NL, Space, IsDot); +} + +void RangeConstraintManager::printConstraints(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + unsigned int Space, + bool IsDot) const { ConstraintRangeTy Constraints = State->get<ConstraintRange>(); Indent(Out, Space, IsDot) << "\"constraints\": "; @@ -2634,3 +2667,140 @@ void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State, --Space; Indent(Out, Space, IsDot) << "]," << NL; } + +static std::string toString(const SymbolRef &Sym) { + std::string S; + llvm::raw_string_ostream O(S); + Sym->dumpToStream(O); + return O.str(); +} + +static std::string toString(ProgramStateRef State, EquivalenceClass Class) { + SymbolSet ClassMembers = Class.getClassMembers(State); + llvm::SmallVector<SymbolRef, 8> ClassMembersSorted(ClassMembers.begin(), + ClassMembers.end()); + llvm::sort(ClassMembersSorted, + [](const SymbolRef &LHS, const SymbolRef &RHS) { + return toString(LHS) < toString(RHS); + }); + + bool FirstMember = true; + + std::string Str; + llvm::raw_string_ostream Out(Str); + Out << "[ "; + for (SymbolRef ClassMember : ClassMembersSorted) { + if (FirstMember) + FirstMember = false; + else + Out << ", "; + Out << "\"" << ClassMember << "\""; + } + Out << " ]"; + return Out.str(); +} + +void RangeConstraintManager::printEquivalenceClasses(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + unsigned int Space, + bool IsDot) const { + ClassMembersTy Members = State->get<ClassMembers>(); + + Indent(Out, Space, IsDot) << "\"equivalence_classes\": "; + if (Members.isEmpty()) { + Out << "null," << NL; + return; + } + + std::set<std::string> MembersStr; + for (std::pair<EquivalenceClass, SymbolSet> ClassToSymbolSet : Members) + MembersStr.insert(toString(State, ClassToSymbolSet.first)); + + ++Space; + Out << '[' << NL; + bool FirstClass = true; + for (const std::string &Str : MembersStr) { + if (FirstClass) { + FirstClass = false; + } else { + Out << ','; + Out << NL; + } + Indent(Out, Space, IsDot); + Out << Str; + } + Out << NL; + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; +} + +void RangeConstraintManager::printDisequalities(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + unsigned int Space, + bool IsDot) const { + DisequalityMapTy Disequalities = State->get<DisequalityMap>(); + + Indent(Out, Space, IsDot) << "\"disequality_info\": "; + if (Disequalities.isEmpty()) { + Out << "null," << NL; + return; + } + + // Transform the disequality info to an ordered map of + // [string -> (ordered set of strings)] + using EqClassesStrTy = std::set<std::string>; + using DisequalityInfoStrTy = std::map<std::string, EqClassesStrTy>; + DisequalityInfoStrTy DisequalityInfoStr; + for (std::pair<EquivalenceClass, ClassSet> ClassToDisEqSet : Disequalities) { + EquivalenceClass Class = ClassToDisEqSet.first; + ClassSet DisequalClasses = ClassToDisEqSet.second; + EqClassesStrTy MembersStr; + for (EquivalenceClass DisEqClass : DisequalClasses) + MembersStr.insert(toString(State, DisEqClass)); + DisequalityInfoStr.insert({toString(State, Class), MembersStr}); + } + + ++Space; + Out << '[' << NL; + bool FirstClass = true; + for (std::pair<std::string, EqClassesStrTy> ClassToDisEqSet : + DisequalityInfoStr) { + const std::string &Class = ClassToDisEqSet.first; + if (FirstClass) { + FirstClass = false; + } else { + Out << ','; + Out << NL; + } + Indent(Out, Space, IsDot) << "{" << NL; + unsigned int DisEqSpace = Space + 1; + Indent(Out, DisEqSpace, IsDot) << "\"class\": "; + Out << Class; + const EqClassesStrTy &DisequalClasses = ClassToDisEqSet.second; + if (!DisequalClasses.empty()) { + Out << "," << NL; + Indent(Out, DisEqSpace, IsDot) << "\"disequal_to\": [" << NL; + unsigned int DisEqClassSpace = DisEqSpace + 1; + Indent(Out, DisEqClassSpace, IsDot); + bool FirstDisEqClass = true; + for (const std::string &DisEqClass : DisequalClasses) { + if (FirstDisEqClass) { + FirstDisEqClass = false; + } else { + Out << ',' << NL; + Indent(Out, DisEqClassSpace, IsDot); + } + Out << DisEqClass; + } + Out << "]" << NL; + } + Indent(Out, Space, IsDot) << "}"; + } + Out << NL; + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; +} diff --git a/clang/test/Analysis/expr-inspection-printState-diseq-info.c b/clang/test/Analysis/expr-inspection-printState-diseq-info.c new file mode 100644 index 0000000000000..8db3722a8f263 --- /dev/null +++ b/clang/test/Analysis/expr-inspection-printState-diseq-info.c @@ -0,0 +1,34 @@ +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=debug.ExprInspection %s 2>&1 | FileCheck %s + +void clang_analyzer_printState(); + +void test_disequality_info(int e0, int b0, int b1, int c0) { + int e1 = e0 - b0; + if (b0 == 2) { + int e2 = e1 - b1; + if (e2 > 0) { + if (b1 != c0) + clang_analyzer_printState(); + } + } +} + + // CHECK: "disequality_info": [ + // CHECK-NEXT: { + // CHECK-NEXT: "class": [ "(reg_$0<int e0>) - 2" ], + // CHECK-NEXT: "disequal_to": [ + // CHECK-NEXT: [ "reg_$2<int b1>" ]] + // CHECK-NEXT: }, + // CHECK-NEXT: { + // CHECK-NEXT: "class": [ "reg_$2<int b1>" ], + // CHECK-NEXT: "disequal_to": [ + // CHECK-NEXT: [ "(reg_$0<int e0>) - 2" ], + // CHECK-NEXT: [ "reg_$3<int c0>" ]] + // CHECK-NEXT: }, + // CHECK-NEXT: { + // CHECK-NEXT: "class": [ "reg_$3<int c0>" ], + // CHECK-NEXT: "disequal_to": [ + // CHECK-NEXT: [ "reg_$2<int b1>" ]] + // CHECK-NEXT: } + // CHECK-NEXT: ], diff --git a/clang/test/Analysis/expr-inspection-printState-eq-classes.c b/clang/test/Analysis/expr-inspection-printState-eq-classes.c new file mode 100644 index 0000000000000..34514335f9e80 --- /dev/null +++ b/clang/test/Analysis/expr-inspection-printState-eq-classes.c @@ -0,0 +1,21 @@ +// RUN: %clang_analyze_cc1 \ +// RUN: -analyzer-checker=debug.ExprInspection %s 2>&1 | FileCheck %s + +void clang_analyzer_printState(); + +void test_equivalence_classes(int a, int b, int c, int d) { + if (a + b != c) + return; + if (a != d) + return; + if (b != 0) + return; + clang_analyzer_printState(); + (void)(a * b * c * d); + return; +} + + // CHECK: "equivalence_classes": [ + // CHECK-NEXT: [ "((reg_$0<int a>) + (reg_$1<int b>)) != (reg_$2<int c>)", "(reg_$0<int a>) != (reg_$2<int c>)" ], + // CHECK-NEXT: [ "(reg_$0<int a>) + (reg_$1<int b>)", "reg_$0<int a>", "reg_$2<int c>", "reg_$3<int d>" ] + // CHECK-NEXT: ], diff --git a/clang/test/Analysis/expr-inspection.c b/clang/test/Analysis/expr-inspection.c index 283fa9bdb724a..76118a76e71ca 100644 --- a/clang/test/Analysis/expr-inspection.c +++ b/clang/test/Analysis/expr-inspection.c @@ -38,6 +38,8 @@ void foo(int x) { // CHECK-NEXT: "constraints": [ // CHECK-NEXT: { "symbol": "reg_$0<int x>", "range": "{ [-2147483648, 13] }" } // CHECK-NEXT: ], +// CHECK-NEXT: "equivalence_classes": null, +// CHECK-NEXT: "disequality_info": null, // CHECK-NEXT: "dynamic_types": null, // CHECK-NEXT: "dynamic_casts": null, // CHECK-NEXT: "constructing_objects": null, _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits