From b56b15ed719b4b6a5fe1a0a8b5ece54467ab2fee Mon Sep 17 00:00:00 2001 From: Sam McCall Date: Fri, 21 Apr 2023 21:03:43 +0200 Subject: [PATCH] [dataflow] HTMLLogger - show the value of the current expr Differential Revision: https://reviews.llvm.org/D148949 --- .../clang/Analysis/FlowSensitive/Value.h | 11 ++ .../lib/Analysis/FlowSensitive/HTMLLogger.cpp | 101 ++++++++++++++++++ .../lib/Analysis/FlowSensitive/HTMLLogger.css | 24 +++++ .../Analysis/FlowSensitive/HTMLLogger.html | 28 +++++ .../lib/Analysis/FlowSensitive/HTMLLogger.js | 3 + .../Analysis/FlowSensitive/LoggerTest.cpp | 1 + 6 files changed, 168 insertions(+) diff --git a/clang/include/clang/Analysis/FlowSensitive/Value.h b/clang/include/clang/Analysis/FlowSensitive/Value.h index 32d10a348948..861a9963e668 100644 --- a/clang/include/clang/Analysis/FlowSensitive/Value.h +++ b/clang/include/clang/Analysis/FlowSensitive/Value.h @@ -73,6 +73,11 @@ public: Properties.insert_or_assign(Name, &Val); } + llvm::iterator_range::const_iterator> + properties() const { + return {Properties.begin(), Properties.end()}; + } + private: Kind ValKind; llvm::StringMap Properties; @@ -307,6 +312,12 @@ public: /// Assigns `Val` as the child value for `D`. void setChild(const ValueDecl &D, Value &Val) { Children[&D] = &Val; } + llvm::iterator_range< + llvm::DenseMap::const_iterator> + children() const { + return {Children.begin(), Children.end()}; + } + private: llvm::DenseMap Children; }; diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp index b229194bc8f4..14293a3043f9 100644 --- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp +++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp @@ -67,6 +67,7 @@ #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Program.h" +#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/raw_ostream.h" // Defines assets: HTMLLogger_{html_js,css} #include "HTMLLogger.inc" @@ -79,6 +80,96 @@ llvm::Expected renderSVG(llvm::StringRef DotGraph); using StreamFactory = std::function()>; +// Recursively dumps Values/StorageLocations as JSON +class ModelDumper { +public: + ModelDumper(llvm::json::OStream &JOS, const Environment &Env) + : JOS(JOS), Env(Env) {} + + void dump(Value &V) { + JOS.attribute("value_id", llvm::to_string(&V)); + if (!Visited.insert(&V).second) + return; + + JOS.attribute("kind", debugString(V.getKind())); + + switch (V.getKind()) { + case Value::Kind::Integer: + case Value::Kind::TopBool: + case Value::Kind::AtomicBool: + break; + case Value::Kind::Reference: + JOS.attributeObject( + "referent", [&] { dump(cast(V).getReferentLoc()); }); + break; + case Value::Kind::Pointer: + JOS.attributeObject( + "pointee", [&] { dump(cast(V).getPointeeLoc()); }); + break; + case Value::Kind::Struct: + for (const auto &Child : cast(V).children()) + JOS.attributeObject("f:" + Child.first->getNameAsString(), + [&] { dump(*Child.second); }); + break; + case Value::Kind::Disjunction: { + auto &VV = cast(V); + JOS.attributeObject("lhs", [&] { dump(VV.getLeftSubValue()); }); + JOS.attributeObject("rhs", [&] { dump(VV.getRightSubValue()); }); + break; + } + case Value::Kind::Conjunction: { + auto &VV = cast(V); + JOS.attributeObject("lhs", [&] { dump(VV.getLeftSubValue()); }); + JOS.attributeObject("rhs", [&] { dump(VV.getRightSubValue()); }); + break; + } + case Value::Kind::Negation: { + auto &VV = cast(V); + JOS.attributeObject("not", [&] { dump(VV.getSubVal()); }); + break; + } + case Value::Kind::Implication: { + auto &VV = cast(V); + JOS.attributeObject("if", [&] { dump(VV.getLeftSubValue()); }); + JOS.attributeObject("then", [&] { dump(VV.getRightSubValue()); }); + break; + } + case Value::Kind::Biconditional: { + auto &VV = cast(V); + JOS.attributeObject("lhs", [&] { dump(VV.getLeftSubValue()); }); + JOS.attributeObject("rhs", [&] { dump(VV.getRightSubValue()); }); + break; + } + } + + for (const auto& Prop : V.properties()) + JOS.attributeObject(("p:" + Prop.first()).str(), + [&] { dump(*Prop.second); }); + + // Running the SAT solver is expensive, but knowing which booleans are + // guaranteed true/false here is valuable and hard to determine by hand. + if (auto *B = llvm::dyn_cast(&V)) { + JOS.attribute("truth", Env.flowConditionImplies(*B) ? "true" + : Env.flowConditionImplies(Env.makeNot(*B)) + ? "false" + : "unknown"); + } + } + void dump(const StorageLocation &L) { + JOS.attribute("location", llvm::to_string(&L)); + if (!Visited.insert(&L).second) + return; + + JOS.attribute("type", L.getType().getAsString()); + if (auto *V = Env.getValue(L)) + dump(*V); + } + + llvm::DenseSet Visited; + llvm::json::OStream &JOS; + const Environment &Env; +}; + class HTMLLogger : public Logger { StreamFactory Streams; std::unique_ptr OS; @@ -184,6 +275,16 @@ public: JOS->attribute("block", blockID(Block)); JOS->attribute("iter", Iter); JOS->attribute("element", ElementIndex); + + // If this state immediately follows an Expr, show its built-in model. + if (ElementIndex > 0) { + auto S = + Iters.back().first->Elements[ElementIndex - 1].getAs(); + if (const Expr *E = S ? llvm::dyn_cast(S->getStmt()) : nullptr) + if (auto *Loc = State.Env.getStorageLocation(*E, SkipPast::None)) + JOS->attributeObject( + "value", [&] { ModelDumper(*JOS, State.Env).dump(*Loc); }); + } if (!ContextLogs.empty()) { JOS->attribute("logs", ContextLogs); ContextLogs.clear(); diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.css b/clang/lib/Analysis/FlowSensitive/HTMLLogger.css index 4877c1264c83..c8212df1f94b 100644 --- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.css +++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.css @@ -116,3 +116,27 @@ code.line:has(.bb-select):before { position: relative; margin-left: 0.5em; } + +.value { + border: 1px solid #888; + font-size: x-small; + flex-grow: 1; +} +.value summary { + background-color: #ace; + display: flex; + justify-content: space-between; +} +.value .address { + font-size: xx-small; + font-family: monospace; + color: #888; +} +.value .property { + display: flex; + margin-top: 0.5em; +} +.value .property .key { + font-weight: bold; + min-width: 5em; +} diff --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.html b/clang/lib/Analysis/FlowSensitive/HTMLLogger.html index 99d4debd05be..a60259a99cce 100644 --- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.html +++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.html @@ -10,6 +10,30 @@ + + + @@ -56,6 +80,10 @@ +