[clang] b56b15e - [dataflow] HTMLLogger - show the value of the current expr

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 26 08:15:31 PDT 2023


Author: Sam McCall
Date: 2023-04-26T17:15:23+02:00
New Revision: b56b15ed719b4b6a5fe1a0a8b5ece54467ab2fee

URL: https://github.com/llvm/llvm-project/commit/b56b15ed719b4b6a5fe1a0a8b5ece54467ab2fee
DIFF: https://github.com/llvm/llvm-project/commit/b56b15ed719b4b6a5fe1a0a8b5ece54467ab2fee.diff

LOG: [dataflow] HTMLLogger - show the value of the current expr

Differential Revision: https://reviews.llvm.org/D148949

Added: 
    

Modified: 
    clang/include/clang/Analysis/FlowSensitive/Value.h
    clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
    clang/lib/Analysis/FlowSensitive/HTMLLogger.css
    clang/lib/Analysis/FlowSensitive/HTMLLogger.html
    clang/lib/Analysis/FlowSensitive/HTMLLogger.js
    clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Analysis/FlowSensitive/Value.h b/clang/include/clang/Analysis/FlowSensitive/Value.h
index 32d10a3489483..861a9963e6689 100644
--- a/clang/include/clang/Analysis/FlowSensitive/Value.h
+++ b/clang/include/clang/Analysis/FlowSensitive/Value.h
@@ -73,6 +73,11 @@ class Value {
     Properties.insert_or_assign(Name, &Val);
   }
 
+  llvm::iterator_range<llvm::StringMap<Value *>::const_iterator>
+  properties() const {
+    return {Properties.begin(), Properties.end()};
+  }
+
 private:
   Kind ValKind;
   llvm::StringMap<Value *> Properties;
@@ -307,6 +312,12 @@ class StructValue final : public Value {
   /// Assigns `Val` as the child value for `D`.
   void setChild(const ValueDecl &D, Value &Val) { Children[&D] = &Val; }
 
+  llvm::iterator_range<
+      llvm::DenseMap<const ValueDecl *, Value *>::const_iterator>
+  children() const {
+    return {Children.begin(), Children.end()};
+  }
+
 private:
   llvm::DenseMap<const ValueDecl *, Value *> Children;
 };

diff  --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp b/clang/lib/Analysis/FlowSensitive/HTMLLogger.cpp
index b229194bc8f44..14293a3043f98 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<std::string> renderSVG(llvm::StringRef DotGraph);
 
 using StreamFactory = std::function<std::unique_ptr<llvm::raw_ostream>()>;
 
+// 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<ReferenceValue>(V).getReferentLoc()); });
+      break;
+    case Value::Kind::Pointer:
+      JOS.attributeObject(
+          "pointee", [&] { dump(cast<PointerValue>(V).getPointeeLoc()); });
+      break;
+    case Value::Kind::Struct:
+      for (const auto &Child : cast<StructValue>(V).children())
+        JOS.attributeObject("f:" + Child.first->getNameAsString(),
+                            [&] { dump(*Child.second); });
+      break;
+    case Value::Kind::Disjunction: {
+      auto &VV = cast<DisjunctionValue>(V);
+      JOS.attributeObject("lhs", [&] { dump(VV.getLeftSubValue()); });
+      JOS.attributeObject("rhs", [&] { dump(VV.getRightSubValue()); });
+      break;
+    }
+    case Value::Kind::Conjunction: {
+      auto &VV = cast<ConjunctionValue>(V);
+      JOS.attributeObject("lhs", [&] { dump(VV.getLeftSubValue()); });
+      JOS.attributeObject("rhs", [&] { dump(VV.getRightSubValue()); });
+      break;
+    }
+    case Value::Kind::Negation: {
+      auto &VV = cast<NegationValue>(V);
+      JOS.attributeObject("not", [&] { dump(VV.getSubVal()); });
+      break;
+    }
+    case Value::Kind::Implication: {
+      auto &VV = cast<ImplicationValue>(V);
+      JOS.attributeObject("if", [&] { dump(VV.getLeftSubValue()); });
+      JOS.attributeObject("then", [&] { dump(VV.getRightSubValue()); });
+      break;
+    }
+    case Value::Kind::Biconditional: {
+      auto &VV = cast<BiconditionalValue>(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<BoolValue>(&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<const void*> Visited;
+  llvm::json::OStream &JOS;
+  const Environment &Env;
+};
+
 class HTMLLogger : public Logger {
   StreamFactory Streams;
   std::unique_ptr<llvm::raw_ostream> OS;
@@ -184,6 +275,16 @@ class HTMLLogger : public Logger {
       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<CFGStmt>();
+        if (const Expr *E = S ? llvm::dyn_cast<Expr>(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 4877c1264c83b..c8212df1f94b8 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 99d4debd05be3..a60259a99cce0 100644
--- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.html
+++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.html
@@ -10,6 +10,30 @@
 
 <head>
 <?INJECT?>
+
+<template id="value-template">
+  <details class="value" open>
+    <summary>
+      <span>{{v.kind}}
+        <template data-if="v.value_id"><span class="address">#{{v.value_id}}</span></template>
+      </span>
+      <template data-if="v.location">
+        <span>{{v.type}} <span class="address">@{{v.location}}</span></span>
+      </template>
+    </summary>
+    <template
+        data-for="kv in Object.entries(v)"
+        data-if="['kind', 'value_id', 'type', 'location'].indexOf(kv[0]) < 0">
+      <div class="property"><span class="key">{{kv[0]}}</span>
+        <template data-if="typeof(kv[1]) != 'object'">{{kv[1]}}</template>
+        <template data-if="typeof(kv[1]) == 'object'" data-let="v = kv[1]">
+          <template data-use="value-template"></template>
+        </template>
+      </div>
+    </template>
+  </details>
+</template>
+
 </head>
 
 <body>
@@ -56,6 +80,10 @@
   <template data-if="state.element == 0">{{state.block}} (iteration {{state.iter}}) initial state</template>
   <template data-if="state.element != 0">Element {{selection.elt}} (iteration {{state.iter}})</template>
 </header>
+<template data-if="state.value" data-let="v = state.value">
+  <h2>Value</h2>
+  <template data-use="value-template"></template>
+</template>
 <template data-if="state.logs">
   <h2>Logs</h2>
   <pre>{{state.logs}}</pre>

diff  --git a/clang/lib/Analysis/FlowSensitive/HTMLLogger.js b/clang/lib/Analysis/FlowSensitive/HTMLLogger.js
index 5958418bc1e93..6e04bc00f6631 100644
--- a/clang/lib/Analysis/FlowSensitive/HTMLLogger.js
+++ b/clang/lib/Analysis/FlowSensitive/HTMLLogger.js
@@ -73,6 +73,9 @@ function inflate(tmpl, data, parent, next) {
     }
     return parent.insertBefore(clone, next);
   }
+  // data-use="xyz": use <template id="xyz"> instead. (Allows recursion.)
+  if ('use' in tmpl.dataset)
+    return inflate(document.getElementById(tmpl.dataset.use), data, parent, next);
   // <template> tag handling. Base case: recursively inflate.
   function handle(data) {
     for (c of tmpl.content.childNodes)

diff  --git a/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp b/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
index 0b8579a45b2df..7ba19b86dd102 100644
--- a/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/LoggerTest.cpp
@@ -176,6 +176,7 @@ TEST(LoggerTest, HTML) {
       << "has analysis point state";
   EXPECT_THAT(Logs[0], HasSubstr("transferBranch(0)")) << "has analysis logs";
   EXPECT_THAT(Logs[0], HasSubstr("LocToVal")) << "has built-in lattice dump";
+  EXPECT_THAT(Logs[0], HasSubstr("\"type\": \"int\"")) << "has value dump";
 }
 
 } // namespace


        


More information about the cfe-commits mailing list